From 3c8fa3452ed9fb41ce9d3f68c81bcc4b38d19612 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期一, 31 八月 2026 20:14:41 +0800
Subject: [PATCH] fix: 统一销售和采购相关页面金额、单价、数量、总价保留6位小数

---
 src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java |  729 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 693 insertions(+), 36 deletions(-)

diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
index 09d5d20..3c76a76 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -6,8 +6,12 @@
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.approve.bean.vo.ApproveProcessVO;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
 import com.ruoyi.approve.pojo.ApproveProcess;
+import com.ruoyi.approve.service.ApprovalInstanceService;
 import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
 import com.ruoyi.basic.enums.ApplicationTypeEnum;
 import com.ruoyi.basic.enums.RecordTypeEnum;
@@ -18,16 +22,21 @@
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.basic.pojo.SupplierManage;
 import com.ruoyi.basic.utils.FileUtil;
-import com.ruoyi.common.enums.FileNameType;
+import com.ruoyi.common.enums.ApprovalStatusEnum;
+import com.ruoyi.common.enums.ReviewStatusEnum;
+import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.framework.web.domain.R;
 import com.ruoyi.other.mapper.TempFileMapper;
 import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
+import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.purchase.dto.PurchaseLedgerDto;
@@ -44,13 +53,15 @@
 import com.ruoyi.quality.pojo.QualityInspectParam;
 import com.ruoyi.quality.pojo.QualityTestStandard;
 import com.ruoyi.quality.pojo.QualityTestStandardParam;
+import com.ruoyi.quality.service.IQualityInspectService;
 import com.ruoyi.sales.mapper.CommonFileMapper;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
-import com.ruoyi.sales.pojo.CommonFile;
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
@@ -100,6 +111,11 @@
     private final ApproveProcessServiceImpl approveProcessService;
     private final ProcurementRecordMapper procurementRecordStorageMapper;
     private final FileUtil fileUtil;
+    private final ApprovalInstanceService approvalInstanceService;
+    private final IQualityInspectService qualityInspectService;
+    private final StockInRecordService stockInRecordService;
+    private final StockUtils stockUtils;
+    private final ApprovalTemplateMapper approvalTemplateMapper;
 
     @Override
     public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
@@ -113,6 +129,7 @@
         if (purchaseLedger.getApprovalStatus() != null) {
             queryWrapper.eq(PurchaseLedger::getApprovalStatus, purchaseLedger.getApprovalStatus());
         }
+        queryWrapper.orderByDesc(PurchaseLedger::getEntryDate);
         return purchaseLedgerMapper.selectList(queryWrapper);
     }
 
@@ -148,6 +165,9 @@
         purchaseLedger.setApprovalStatus(1);
         // 3. 鏂板鎴栨洿鏂颁富琛�
         if (purchaseLedger.getId() == null) {
+            if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
+                purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
+            }
             purchaseLedgerMapper.insert(purchaseLedger);
         } else {
             // 鍒犻櫎閲囪喘瀹℃牳锛岄噸鏂版彁浜�
@@ -172,6 +192,188 @@
         // 5. 杩佺Щ涓存椂鏂囦欢鍒版寮忕洰褰�
         fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS());
         return 1;
+    }
+
+    @Override
+    public R batchInsertPurchaseSteps(List<Long> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return R.fail("璇烽�夋嫨閲囪喘鍙拌处");
+        }
+
+        List<Long> distinctIds = ids.stream()
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(distinctIds)) {
+            return R.fail("璇烽�夋嫨閲囪喘鍙拌处");
+        }
+
+        PurchaseLedgerDto queryDto = new PurchaseLedgerDto();
+        queryDto.setIds(distinctIds);
+        IPage<PurchaseLedgerDto> pageResult = this.selectPurchaseLedgerListPage(
+                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, distinctIds.size()),
+                queryDto
+        );
+        List<PurchaseLedgerDto> ledgerDtos = pageResult == null || pageResult.getRecords() == null
+                ? Collections.emptyList()
+                : pageResult.getRecords();
+
+        Map<Long, PurchaseLedgerDto> ledgerDtoMap = ledgerDtos.stream()
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(PurchaseLedgerDto::getId, item -> item, (left, right) -> left, LinkedHashMap::new));
+
+        List<Map<String, Object>> details = new ArrayList<>();
+        int successCount = 0;
+        int skipCount = 0;
+        int failCount = 0;
+        int autoApprovedCount = 0;
+
+        for (Long id : distinctIds) {
+            Map<String, Object> detail = new LinkedHashMap<>();
+            detail.put("purchaseLedgerId", id);
+            PurchaseLedgerDto purchaseLedgerDto = ledgerDtoMap.get(id);
+            if (purchaseLedgerDto == null) {
+                failCount++;
+                detail.put("status", "FAIL");
+                detail.put("message", "閲囪喘鍙拌处涓嶅瓨鍦ㄦ垨鏈煡璇㈠埌");
+                details.add(detail);
+                continue;
+            }
+
+            detail.put("purchaseContractNumber", purchaseLedgerDto.getPurchaseContractNumber());
+            detail.put("approvalStatus", purchaseLedgerDto.getApprovalStatus());
+            detail.put("stockInStatus", purchaseLedgerDto.getStockInStatus());
+
+            if (ApprovalStatusEnum.REJECTED.getCode().equals(purchaseLedgerDto.getApprovalStatus())) {
+                skipCount++;
+                detail.put("status", "SKIP");
+                detail.put("message", "閲囪喘鍗曞凡椹冲洖");
+                details.add(detail);
+                continue;
+            }
+
+            if ("瀹屽叏鍏ュ簱".equals(purchaseLedgerDto.getStockInStatus())) {
+                skipCount++;
+                detail.put("status", "SKIP");
+                detail.put("message", "閲囪喘鍗曞凡瀹屽叏鍏ュ簱");
+                details.add(detail);
+                continue;
+            }
+
+            try {
+                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
+                if (purchaseLedger == null) {
+                    failCount++;
+                    detail.put("status", "FAIL");
+                    detail.put("message", "閲囪喘鍙拌处涓嶅瓨鍦�");
+                    details.add(detail);
+                    continue;
+                }
+
+                if (!ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
+                    ApprovalInstance approvalInstance = approvalInstanceService.getOne(
+                            Wrappers.<ApprovalInstance>lambdaQuery()
+                                    .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
+                                    .eq(ApprovalInstance::getBusinessType, 5L)
+                                    .eq(ApprovalInstance::getDeleted, 0)
+                                    .orderByDesc(ApprovalInstance::getId)
+                                    .last("limit 1")
+                    );
+                    if (approvalInstance == null) {
+                        failCount++;
+                        detail.put("status", "FAIL");
+                        detail.put("message", "鏈壘鍒板搴旂殑閲囪喘瀹℃壒瀹炰緥");
+                        details.add(detail);
+                        continue;
+                    }
+
+                    if ("APPROVED".equals(approvalInstance.getStatus())
+                            && !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
+                        purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode());
+                        purchaseLedgerMapper.updateById(purchaseLedger);
+                    } else if (!"APPROVED".equals(approvalInstance.getStatus())) {
+                        R autoApproveResult = approvalInstanceService.autoApprove(approvalInstance.getId());
+                        if (autoApproveResult == null || !R.isSuccess(autoApproveResult)) {
+                            failCount++;
+                            detail.put("status", "FAIL");
+                            detail.put("message", autoApproveResult == null ? "閲囪喘瀹℃壒鑷姩閫氳繃澶辫触" : autoApproveResult.getMsg());
+                            details.add(detail);
+                            continue;
+                        }
+                        autoApprovedCount++;
+                    }
+
+                    purchaseLedger = purchaseLedgerMapper.selectById(id);
+                    if (purchaseLedger == null || !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
+                        failCount++;
+                        detail.put("status", "FAIL");
+                        detail.put("message", "閲囪喘鍗曞鎵圭姸鎬佹湭鏇存柊涓哄凡閫氳繃");
+                        details.add(detail);
+                        continue;
+                    }
+                }
+
+                List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(
+                        Wrappers.<SalesLedgerProduct>lambdaQuery()
+                                .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
+                                .eq(SalesLedgerProduct::getType, 2)
+                                .orderByAsc(SalesLedgerProduct::getId)
+                );
+                if (CollectionUtils.isEmpty(products)) {
+                    skipCount++;
+                    detail.put("status", "SKIP");
+                    detail.put("message", "閲囪喘鍗曟病鏈変骇鍝佹槑缁�");
+                    details.add(detail);
+                    continue;
+                }
+
+                int processedProductCount = 0;
+                int skippedProductCount = 0;
+                int failedProductCount = 0;
+                for (SalesLedgerProduct product : products) {
+                    try {
+                        boolean processed;
+                        if (Boolean.TRUE.equals(product.getIsChecked())) {
+                            processed = processPurchaseQualityProduct(purchaseLedger, product);
+                        } else {
+                            processed = processPurchaseDirectProduct(purchaseLedger, product);
+                        }
+                        if (processed) {
+                            processedProductCount++;
+                        } else {
+                            skippedProductCount++;
+                        }
+                    } catch (Exception ex) {
+                        failedProductCount++;
+                        log.error("鎵归噺鎺ㄨ繘閲囪喘鍙拌处澶辫触, purchaseLedgerId={}, productId={}, productModelId={}",
+                                purchaseLedger.getId(), product.getId(), product.getProductModelId(), ex);
+                    }
+                }
+
+                successCount++;
+                detail.put("status", failedProductCount > 0 ? "PARTIAL" : "SUCCESS");
+                detail.put("processedProductCount", processedProductCount);
+                detail.put("skippedProductCount", skippedProductCount);
+                detail.put("failedProductCount", failedProductCount);
+                detail.put("message", failedProductCount > 0 ? "閮ㄥ垎浜у搧澶勭悊澶辫触" : "澶勭悊瀹屾垚");
+                details.add(detail);
+            } catch (Exception ex) {
+                failCount++;
+                detail.put("status", "FAIL");
+                detail.put("message", ex.getMessage());
+                details.add(detail);
+                log.error("鎵归噺鎺ㄨ繘閲囪喘鍙拌处澶辫触, purchaseLedgerId={}", id, ex);
+            }
+        }
+
+        Map<String, Object> summary = new LinkedHashMap<>();
+        summary.put("totalCount", distinctIds.size());
+        summary.put("successCount", successCount);
+        summary.put("skipCount", skipCount);
+        summary.put("failCount", failCount);
+        summary.put("autoApprovedCount", autoApprovedCount);
+        summary.put("details", details);
+        return R.ok(summary, "鎵归噺鎺ㄨ繘閲囪喘姝ラ瀹屾垚");
     }
 
 
@@ -199,7 +401,219 @@
                         param.setId(null);
                         param.setInspectId(qualityInspect.getId());
                         qualityInspectParamMapper.insert(param);
-                    });
+            });
+        }
+    }
+
+    private boolean processPurchaseQualityProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) {
+        if (purchaseLedger == null || product == null || product.getProductModelId() == null) {
+            return false;
+        }
+
+        QualityInspect qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId());
+        if (qualityInspect == null) {
+            addQualityInspect(purchaseLedger, product);
+            qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId());
+        }
+        if (qualityInspect == null) {
+            return false;
+        }
+
+        LocalDateTime purchaseInspectTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1);
+        if (purchaseInspectTime != null && (qualityInspect.getCheckTime() == null
+                || !DateUtils.toLocalDate(qualityInspect.getCheckTime()).equals(purchaseInspectTime.toLocalDate()))) {
+            qualityInspect.setCheckTime(DateUtils.toDate(purchaseInspectTime.toLocalDate()));
+            qualityInspectMapper.updateById(qualityInspect);
+        }
+
+        List<StockInRecord> stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
+        if (hasApprovedStockRecord(stockRecords)) {
+            return true;
+        }
+
+        if (!Integer.valueOf(1).equals(qualityInspect.getInspectState())) {
+            R autoSubmitResult = qualityInspectService.autoSubmit(qualityInspect.getId());
+            if (autoSubmitResult == null || !R.isSuccess(autoSubmitResult)) {
+                return false;
+            }
+            qualityInspect = qualityInspectMapper.selectById(qualityInspect.getId());
+        }
+
+        stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
+        if (CollectionUtils.isEmpty(stockRecords)
+                && qualityInspect.getQualifiedQuantity() != null
+                && qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
+            stockUtils.addStockWithBatchNo(
+                    product.getProductModelId(),
+                    qualityInspect.getQualifiedQuantity(),
+                    StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode(),
+                    qualityInspect.getId(),
+                    null,
+                    purchaseInspectTime == null ? null : purchaseInspectTime.plusDays(1)
+            );
+            stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
+        }
+
+        StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords);
+        if (targetStockRecord == null) {
+            return false;
+        }
+
+        LocalDateTime qualityStockCreateTime = resolveQualityStockCreateTime(qualityInspect);
+        if (qualityStockCreateTime != null && (targetStockRecord.getCreateTime() == null
+                || !qualityStockCreateTime.equals(targetStockRecord.getCreateTime()))) {
+            targetStockRecord.setCreateTime(qualityStockCreateTime);
+            stockInRecordService.updateById(targetStockRecord);
+        }
+
+        approveStockRecords(Collections.singletonList(targetStockRecord));
+        stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
+        return hasApprovedStockRecord(stockRecords);
+    }
+
+    private boolean processPurchaseDirectProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) {
+        if (purchaseLedger == null || product == null || product.getProductModelId() == null) {
+            return false;
+        }
+        if (product.getQuantity() == null) {
+            return false;
+        }
+        if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
+            return false;
+        }
+
+        LocalDateTime stockCreateTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1);
+
+        List<StockInRecord> stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
+        if (hasApprovedStockRecord(stockRecords)) {
+            return true;
+        }
+
+        if (CollectionUtils.isEmpty(stockRecords)) {
+            stockUtils.addStockWithBatchNo(
+                    product.getProductModelId(),
+                    product.getQuantity(),
+                    StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
+                    purchaseLedger.getId(),
+                    purchaseLedger.getPurchaseContractNumber() + "-" + product.getId(),
+                    stockCreateTime
+            );
+            stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
+        }
+
+        if (CollectionUtils.isEmpty(stockRecords)) {
+            return false;
+        }
+
+        StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords);
+        if (targetStockRecord == null) {
+            return false;
+        }
+
+        if (stockCreateTime != null && (targetStockRecord.getCreateTime() == null
+                || !stockCreateTime.equals(targetStockRecord.getCreateTime()))) {
+            targetStockRecord.setCreateTime(stockCreateTime);
+            stockInRecordService.updateById(targetStockRecord);
+        }
+
+        approveStockRecords(Collections.singletonList(targetStockRecord));
+        stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
+        return hasApprovedStockRecord(stockRecords);
+    }
+
+    private LocalDateTime toStartOfDayPlusDays(Date date, int days) {
+        if (date == null) {
+            return null;
+        }
+        return DateUtils.toLocalDate(date).plusDays(days).atStartOfDay();
+    }
+
+    private LocalDateTime resolveQualityStockCreateTime(QualityInspect qualityInspect) {
+        if (qualityInspect == null || qualityInspect.getCheckTime() == null) {
+            return null;
+        }
+        return DateUtils.toLocalDate(qualityInspect.getCheckTime()).plusDays(1).atStartOfDay();
+    }
+
+    private QualityInspect findLatestPurchaseQualityInspect(Long purchaseLedgerId, Long productModelId) {
+        if (purchaseLedgerId == null || productModelId == null) {
+            return null;
+        }
+        return qualityInspectMapper.selectOne(
+                Wrappers.<QualityInspect>lambdaQuery()
+                        .eq(QualityInspect::getInspectType, 0)
+                        .eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId)
+                        .eq(QualityInspect::getProductModelId, productModelId)
+                        .orderByDesc(QualityInspect::getId)
+                        .last("limit 1")
+        );
+    }
+
+    private List<StockInRecord> findQualityStockRecords(Long qualityInspectId, Long productModelId) {
+        if (qualityInspectId == null || productModelId == null) {
+            return Collections.emptyList();
+        }
+        return stockInRecordService.list(
+                Wrappers.<StockInRecord>lambdaQuery()
+                        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode())
+                        .eq(StockInRecord::getRecordId, qualityInspectId)
+                        .eq(StockInRecord::getProductModelId, productModelId)
+                        .orderByDesc(StockInRecord::getId)
+        );
+    }
+
+    private List<StockInRecord> findDirectStockRecords(Long purchaseLedgerId, String purchaseContractNumber, Long productModelId, Long purchaseProductId) {
+        if (purchaseLedgerId == null || productModelId == null || purchaseProductId == null || !StringUtils.hasText(purchaseContractNumber)) {
+            return Collections.emptyList();
+        }
+        String batchNo = purchaseContractNumber + "-" + purchaseProductId;
+        return stockInRecordService.list(
+                Wrappers.<StockInRecord>lambdaQuery()
+                        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode())
+                        .eq(StockInRecord::getRecordId, purchaseLedgerId)
+                        .eq(StockInRecord::getProductModelId, productModelId)
+                        .eq(StockInRecord::getBatchNo, batchNo)
+                        .orderByDesc(StockInRecord::getId)
+        );
+    }
+
+    private boolean hasApprovedStockRecord(List<StockInRecord> stockRecords) {
+        return stockRecords != null && stockRecords.stream()
+                .anyMatch(item -> ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus()));
+    }
+
+    private StockInRecord findLatestUnapprovedStockRecord(List<StockInRecord> stockRecords) {
+        if (CollectionUtils.isEmpty(stockRecords)) {
+            return null;
+        }
+        return stockRecords.stream()
+                .filter(item -> !ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus()))
+                .findFirst()
+                .orElse(null);
+    }
+
+    private void approveStockRecords(List<StockInRecord> stockRecords) {
+        if (CollectionUtils.isEmpty(stockRecords)) {
+            return;
+        }
+        List<Long> rejectedIds = stockRecords.stream()
+                .filter(item -> ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus()))
+                .map(StockInRecord::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        if (!rejectedIds.isEmpty()) {
+            stockInRecordService.batchReAudit(rejectedIds);
+        }
+
+        List<Long> pendingIds = stockRecords.stream()
+                .filter(item -> item.getApprovalStatus() == null
+                        || ReviewStatusEnum.PENDING_REVIEW.getCode().equals(item.getApprovalStatus())
+                        || ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus()))
+                .map(StockInRecord::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        if (!pendingIds.isEmpty()) {
+            stockInRecordService.batchApprove(pendingIds, ReviewStatusEnum.APPROVED.getCode());
         }
     }
 
@@ -455,12 +869,13 @@
     }
 
     @Override
-    public String getPurchaseNo() {
+    public String getPurchaseNo(Date entryDate) {
+        LocalDate localDate = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
         // 鐢熸垚鏃ユ湡鍓嶇紑锛堜緥濡傦細CG20250405锛�
-        String purchaseNo = "CG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+        String purchaseNo = "CG" + localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
 
         // 鏋勫缓 Redis Key锛堟寜澶╁垎闅旓級
-        String redisKey = "purchase_no:" + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
+        String redisKey = "purchase_no:" + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
 
         // 鑾峰彇褰撳墠搴忓彿骞堕�掑锛堝師瀛愭搷浣滐級
         Long sequence = redisTemplate.opsForValue().increment(redisKey);
@@ -496,20 +911,37 @@
             List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
                     salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getRecorderName).collect(Collectors.toList())));
             for (PurchaseLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) {
-                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
+                PurchaseLedger existPurchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                         .eq(PurchaseLedger::getPurchaseContractNumber, salesLedgerImportDto.getPurchaseContractNumber())
                         .last("limit 1"));
-                if(purchaseLedger != null){
+                // 宸插鏍搁�氳繃鐨勯噰璐崟璺宠繃锛屼笉鍐嶉噸澶嶅鍏�
+                if (existPurchaseLedger != null && ApprovalStatusEnum.APPROVED.getCode().equals(existPurchaseLedger.getApprovalStatus())) {
                     continue;
                 }
                 PurchaseLedger salesLedger = new PurchaseLedger();
                 BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
-                // 閫氳繃渚涘簲鍟嗗悕绉版煡璇D
-                salesLedger.setSupplierId(customers.stream()
-                        .filter(customer -> customer.getSupplierName().equals(salesLedger.getSupplierName()))
-                        .findFirst()
-                        .map(SupplierManage::getId)
-                        .orElse(null));
+                // 寰呭鏍哥姸鎬佺殑閲囪喘鍗曪細鍒犻櫎璇ュ崟涓嬬殑浜у搧璇︽儏鍜屽鎵瑰疄渚嬶紝閲嶆柊褰曞叆璇︽儏
+                if (existPurchaseLedger != null) {
+                    salesLedgerProductMapper.delete(new LambdaQueryWrapper<SalesLedgerProduct>()
+                            .eq(SalesLedgerProduct::getSalesLedgerId, existPurchaseLedger.getId())
+                            .eq(SalesLedgerProduct::getType, 2));
+                    // 娓呯悊璇ュ崟涓嬬殑璐ㄦ璁板綍鍙婅川妫�鍙傛暟
+                    deleteQualityInspectByPurchaseLedger(existPurchaseLedger.getId());
+                    ApprovalInstance existApprovalInstance = approvalInstanceService.getOne(
+                            Wrappers.<ApprovalInstance>lambdaQuery()
+                                    .eq(ApprovalInstance::getBusinessId, existPurchaseLedger.getId())
+                                    .eq(ApprovalInstance::getBusinessType, 5L)
+                                    .eq(ApprovalInstance::getDeleted, 0)
+                                    .orderByDesc(ApprovalInstance::getId)
+                                    .last("limit 1"));
+                    if (existApprovalInstance != null) {
+                        approvalInstanceService.delete(Collections.singletonList(existApprovalInstance.getId()));
+                    }
+                    salesLedger.setId(existPurchaseLedger.getId());
+                    salesLedger.setApprovalStatus(ApprovalStatusEnum.PENDING.getCode());
+                }
+                // 閫氳繃渚涘簲鍟嗗悕绉版煡璇D锛屼笉瀛樺湪鍒欒嚜鍔ㄦ柊澧�
+                salesLedger.setSupplierId(findOrCreateSupplier(salesLedger.getSupplierName(), customers));
                 Long aLong = sysUsers.stream()
                         .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getRecorderName()))
                         .findFirst()
@@ -549,7 +981,12 @@
                     String collect = ids.stream().map(Object::toString).collect(Collectors.joining(","));
                     salesLedger.setApproveUserIds(collect);
                 }
-                purchaseLedgerMapper.insert(salesLedger);
+                if (salesLedger.getId() == null) {
+                    purchaseLedgerMapper.insert(salesLedger);
+                } else {
+                    // 寰呭鏍哥姸鎬侀噸鏂板綍鍏ユ椂锛屾洿鏂颁富琛�
+                    purchaseLedgerMapper.updateById(salesLedger);
+                }
 
                 for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) {
                     SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct();
@@ -557,22 +994,25 @@
                     salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                     salesLedgerProduct.setType(2);
                     // 璁$畻涓嶅惈绋庢�讳环
-                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
-                    list.stream()
-                            .filter(map -> map.get("productName").equals(salesLedgerProduct.getProductCategory()) && map.get("model").equals(salesLedgerProduct.getSpecificationModel()))
-                            .findFirst()
-                            .ifPresent(map -> {
-                                salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString()));
-                                salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString()));
-                            });
+                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 6, RoundingMode.HALF_UP));
+                    // 浜у搧澶х被/瑙勬牸鍨嬪彿涓嶅瓨鍦ㄦ椂鑷姩鍒涘缓
+                    String productName = salesLedgerProduct.getProductCategory();
+                    if (StringUtils.isNotBlank(productName)) {
+                        // 瑙勬牸鍨嬪彿涓虹┖鏃剁敤浜у搧澶х被浣滀负瑙勬牸鍨嬪彿
+                        String model = StringUtils.isNotBlank(salesLedgerProduct.getSpecificationModel())
+                                ? salesLedgerProduct.getSpecificationModel()
+                                : productName;
+                        salesLedgerProduct.setSpecificationModel(model);
+                        Long productId = findOrCreateProduct(productName, list);
+                        Long modelId = findOrCreateProductModel(productId, productName, model,
+                                salesLedgerProduct.getUnit(), list);
+                        salesLedgerProduct.setProductId(productId);
+                        salesLedgerProduct.setProductModelId(modelId);
+                    }
                     salesLedgerProduct.setRegister(loginUser.getNickName());
                     salesLedgerProduct.setRegisterDate(LocalDateTime.now());
                     salesLedgerProduct.setApproveStatus(0);
-                    // 鏄惁璐ㄦ鍒ゆ柇
                     salesLedgerProduct.setIsChecked(salesLedgerProductImportDto.getIsChecked() == 1);
-                    if(salesLedgerProductImportDto.getIsChecked() == 1){
-                        addQualityInspect(salesLedger, salesLedgerProduct);
-                    }
                     salesLedgerProductMapper.insert(salesLedgerProduct);
                 }
                 // 閲囪喘瀹℃牳
@@ -584,6 +1024,130 @@
             e.printStackTrace();
         }
         return AjaxResult.success("瀵煎叆澶辫触");
+    }
+
+    /**
+     * 鏌ユ壘鎴栨柊澧炰緵搴斿晢锛歴upplier_manage 琛ㄦ寜 supplier_name 鍖归厤锛屼笉瀛樺湪鍒欐柊澧烇紝缂哄け淇℃伅榛樿鍐�"/"
+     */
+    private Long findOrCreateSupplier(String supplierName, List<SupplierManage> supplierList) {
+        if (StringUtils.isBlank(supplierName)) {
+            return null;
+        }
+        // 鍐呭瓨鍒楄〃涓煡鎵撅紙鏈瀵煎叆宸叉柊澧炵殑涔熷寘鍚湪鍐咃級
+        for (SupplierManage supplier : supplierList) {
+            if (supplierName.equals(supplier.getSupplierName())) {
+                return supplier.getId();
+            }
+        }
+        // 鏁版嵁搴撴煡鎵�
+        SupplierManage existSupplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>()
+                .eq(SupplierManage::getSupplierName, supplierName)
+                .last("limit 1"));
+        if (existSupplier != null) {
+            supplierList.add(existSupplier);
+            return existSupplier.getId();
+        }
+        // 鏂板渚涘簲鍟嗭紝缂哄け淇℃伅锛堢◣鍙枫�佸湴鍧�銆佺數璇濈瓑锛夐粯璁ゅ啓"/"
+        SupplierManage newSupplier = new SupplierManage();
+        newSupplier.setSupplierName(supplierName);
+        newSupplier.setTaxpayerIdentificationNum("/");
+        newSupplier.setCompanyAddress("/");
+        newSupplier.setSupplierType("/");
+        newSupplier.setCompanyPhone("/");
+        newSupplier.setBankAccountName("/");
+        newSupplier.setBankAccountNum("/");
+        newSupplier.setContactUserName("/");
+        newSupplier.setContactUserPhone("/");
+        supplierManageMapper.insert(newSupplier);
+        supplierList.add(newSupplier);
+        return newSupplier.getId();
+    }
+
+    /**
+     * 鏌ユ壘鎴栨柊澧炰骇鍝佸ぇ绫伙細product 琛ㄦ寜 product_name 鍖归厤锛屼笉瀛樺湪鍒欐柊澧烇紝榛樿鐖禝D涓�278
+     */
+    private Long findOrCreateProduct(String productName, List<Map<String, Object>> productAndModelList) {
+        // 鍐呭瓨鍒楄〃涓煡鎵撅紙鏈瀵煎叆宸叉柊澧炵殑涔熷寘鍚湪鍐咃級
+        for (Map<String, Object> map : productAndModelList) {
+            if (map.get("productName") != null && productName.equals(map.get("productName"))) {
+                return Long.valueOf(map.get("id").toString());
+            }
+        }
+        // 鏁版嵁搴撴煡鎵撅紙宸插瓨鍦ㄤ絾鏆傛棤瑙勬牸鍨嬪彿鐨勪骇鍝佷笉鍦ㄥ唴瀛樺垪琛ㄤ腑锛�
+        Product existProduct = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+                .eq(Product::getProductName, productName)
+                .last("limit 1"));
+        if (existProduct != null) {
+            return existProduct.getId();
+        }
+        // 鏂板浜у搧澶х被锛岄粯璁ょ埗ID涓�278
+        Product product = new Product();
+        product.setProductName(productName);
+        product.setParentId(278L);
+        productMapper.insert(product);
+        return product.getId();
+    }
+
+    /**
+     * 鏌ユ壘鎴栨柊澧炰骇鍝佽鏍煎瀷鍙凤細product_model 鎸� 浜у搧ID+瑙勬牸鍨嬪彿 鍖归厤锛屼笉瀛樺湪鍒欐柊澧炪��
+     * 浜у搧缂栫爜 = 瑙勬牸鍨嬪彿 + "_" + 涓変綅娴佹按鍙凤紱瑙勬牸鍨嬪彿涓虹┖鏃剁敤浜у搧澶х被浣滀负瑙勬牸鍨嬪彿銆�
+     */
+    private Long findOrCreateProductModel(Long productId, String productName, String model, String unit,
+                                          List<Map<String, Object>> productAndModelList) {
+        // 鍐呭瓨鍒楄〃涓煡鎵�
+        for (Map<String, Object> map : productAndModelList) {
+            if (map.get("id") != null && Long.valueOf(map.get("id").toString()).equals(productId)
+                    && model.equals(map.get("model"))) {
+                return Long.valueOf(map.get("modelId").toString());
+            }
+        }
+        // 鏁版嵁搴撴煡鎵�
+        ProductModel existProductModel = productModelMapper.selectOne(new LambdaQueryWrapper<ProductModel>()
+                .eq(ProductModel::getProductId, productId)
+                .eq(ProductModel::getModel, model)
+                .last("limit 1"));
+        if (existProductModel != null) {
+            return existProductModel.getId();
+        }
+        // 鏂板瑙勬牸鍨嬪彿锛屼骇鍝佺紪鐮� = 瑙勬牸鍨嬪彿 + "_" + 涓変綅娴佹按鍙凤紙鍚屼骇鍝佷笅鑷锛�
+        long count = productModelMapper.selectCount(new LambdaQueryWrapper<ProductModel>()
+                .eq(ProductModel::getProductId, productId));
+        ProductModel productModel = new ProductModel();
+        productModel.setProductId(productId);
+        productModel.setModel(model);
+        productModel.setUnit(unit);
+        productModel.setProductCode(model + "_" + String.format("%03d", count + 1));
+        productModelMapper.insert(productModel);
+        // 璁板綍鍒板唴瀛樺垪琛紝閬垮厤鏈瀵煎叆鍐呴噸澶嶅垱寤�
+        Map<String, Object> newMap = new HashMap<>();
+        newMap.put("id", productId);
+        newMap.put("modelId", productModel.getId());
+        newMap.put("productName", productName);
+        newMap.put("model", model);
+        productAndModelList.add(newMap);
+        return productModel.getId();
+    }
+
+    /**
+     * 鍒犻櫎閲囪喘鍗曚笅鐨勮川妫�璁板綍鍙婅川妫�鍙傛暟锛堝凡鎻愪氦鐨勬楠屽崟涓嶅厑璁稿垹闄わ級
+     */
+    private void deleteQualityInspectByPurchaseLedger(Long purchaseLedgerId) {
+        LambdaQueryWrapper<QualityInspect> inspectWrapper = new LambdaQueryWrapper<>();
+        inspectWrapper.eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId);
+        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(inspectWrapper);
+        for (QualityInspect qualityInspect : qualityInspects) {
+            if (ObjectUtils.isNotEmpty(qualityInspect.getInspectState()) && qualityInspect.getInspectState().equals(1)) {
+                throw new BaseException("宸叉彁浜ょ殑妫�楠屽崟涓嶈兘鍒犻櫎");
+            }
+        }
+        List<Long> inspectIds = qualityInspects.stream()
+                .map(QualityInspect::getId)
+                .collect(Collectors.toList());
+        if (!inspectIds.isEmpty()) {
+            qualityInspectParamMapper.delete(new LambdaQueryWrapper<QualityInspectParam>()
+                    .in(QualityInspectParam::getInspectId, inspectIds));
+        }
+        qualityInspectMapper.delete(inspectWrapper);
     }
 
     @Override
@@ -616,14 +1180,29 @@
         if (loginUser == null) {
             return;
         }
-        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
-        approveProcessVO.setApproveType(5);
-        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
-        approveProcessVO.setApproveReason(purchaseLedger.getPurchaseContractNumber());
-        approveProcessVO.setApproveUserIds(purchaseLedger.getApproveUserIds());
-        approveProcessVO.setApproveUser(loginUser.getUserId());
-        approveProcessVO.setApproveTime(LocalDate.now().toString());
-        approveProcessService.addApprove(approveProcessVO);
+        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getBusinessType, 5L)
+                        .eq(ApprovalTemplate::getDeleted, 0)
+                        .orderByDesc(ApprovalTemplate::getId)
+                        .last("LIMIT 1")
+        );
+        if (approvalTemplate == null) {
+            throw new BaseException("璇峰厛閰嶇疆閲囪喘瀹℃壒妯℃澘");
+        }
+
+        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
+        approvalInstance.setTemplateId(approvalTemplate.getId());
+        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
+        approvalInstance.setBusinessId(purchaseLedger.getId());
+        approvalInstance.setBusinessType(5L);
+        approvalInstance.setCurrentLevel(1);
+        approvalInstance.setApplicantId(loginUser.getUserId());
+        approvalInstance.setTitle(purchaseLedger.getPurchaseContractNumber()+"瀹℃壒");
+        approvalInstance.setApplicantName(loginUser.getNickName());
+        approvalInstance.setApplyTime(LocalDateTime.now());
+        approvalInstanceService.add(approvalInstance);
+
     }
 
     /**
@@ -647,4 +1226,82 @@
         }
         return sb.toString();
     }
+
+    private static final String PURCHASE_LOCK_PREFIX = "purchase_contract_no:";
+    private static final long PURCHASE_LOCK_WAIT_TIMEOUT = 10;
+    private static final long PURCHASE_LOCK_EXPIRE_TIME = 30;
+
+    private String generatePurchaseContractNo(Date entryDate) {
+        LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now();
+        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
+        String lockKey = PURCHASE_LOCK_PREFIX + datePart;
+        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime();
+
+        try {
+            long startWaitTime = System.currentTimeMillis();
+            while (System.currentTimeMillis() - startWaitTime < PURCHASE_LOCK_WAIT_TIMEOUT * 1000) {
+                Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, PURCHASE_LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
+                if (Boolean.TRUE.equals(locked)) {
+                    break;
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    throw new RuntimeException("鑾峰彇閿佹椂琚腑鏂�", e);
+                }
+            }
+
+            if (Boolean.FALSE.equals(redisTemplate.hasKey(lockKey))) {
+                throw new RuntimeException("鑾峰彇閲囪喘鍚堝悓缂栧彿鐢熸垚閿佸け璐ワ細瓒呮椂");
+            }
+
+            String prefix = "CG-" + datePart + "-";
+            List<PurchaseLedger> existingList = purchaseLedgerMapper.selectList(
+                    new LambdaQueryWrapper<PurchaseLedger>()
+                            .likeRight(PurchaseLedger::getPurchaseContractNumber, prefix)
+                            .select(PurchaseLedger::getPurchaseContractNumber));
+            List<Integer> existingSequences = existingList.stream()
+                    .map(PurchaseLedger::getPurchaseContractNumber)
+                    .filter(Objects::nonNull)
+                    .map(no -> {
+                        int lastDash = no.lastIndexOf('-');
+                        if (lastDash >= 0) {
+                            try {
+                                return Integer.parseInt(no.substring(lastDash + 1));
+                            } catch (NumberFormatException e) {
+                                return 0;
+                            }
+                        }
+                        return 0;
+                    })
+                    .collect(Collectors.toList());
+            int nextSequence = findFirstMissingSequence(existingSequences);
+
+            return prefix + String.format("%03d", nextSequence);
+        } finally {
+            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
+            redisTemplate.execute(
+                    new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
+                    Collections.singletonList(lockKey),
+                    lockValue
+            );
+        }
+    }
+
+    private int findFirstMissingSequence(List<Integer> sequences) {
+        if (sequences.isEmpty()) {
+            return 1;
+        }
+        sequences.sort(Integer::compareTo);
+        int next = 1;
+        for (int seq : sequences) {
+            if (seq == next) {
+                next++;
+            } else if (seq > next) {
+                break;
+            }
+        }
+        return next;
+    }
 }

--
Gitblit v1.9.3