| | |
| | | package com.ruoyi.purchase.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | 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.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | 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; |
| | |
| | | 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.exception.base.BaseException; |
| | | import com.ruoyi.common.enums.ApprovalStatusEnum; |
| | | import com.ruoyi.common.enums.ReviewStatusEnum; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.AmountUtils; |
| | | 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.SysDept; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerImportDto; |
| | |
| | | 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; |
| | |
| | | |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.Instant; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | |
| | | @RequiredArgsConstructor |
| | | public class PurchaseLedgerServiceImpl extends ServiceImpl<PurchaseLedgerMapper, PurchaseLedger> implements IPurchaseLedgerService { |
| | | |
| | | private static final Long IMPORT_PRODUCT_PARENT_ID = 3L; |
| | | |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final SysUserMapper userMapper; |
| | | private final TempFileMapper tempFileMapper; |
| | | private final CommonFileMapper commonFileMapper; |
| | | private final SysDeptMapper sysDeptMapper; |
| | | private final SupplierManageMapper supplierManageMapper; |
| | | private final ProductMapper productMapper; |
| | | private final ProductModelMapper productModelMapper; |
| | |
| | | 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) { |
| | |
| | | if (purchaseLedger.getApprovalStatus() != null) { |
| | | queryWrapper.eq(PurchaseLedger::getApprovalStatus, purchaseLedger.getApprovalStatus()); |
| | | } |
| | | // 只查询入库已审批通过的采购台账 |
| | | queryWrapper.inSql(PurchaseLedger::getId, |
| | | "SELECT DISTINCT record_id FROM stock_in_record WHERE approval_status = 1"); |
| | | return purchaseLedgerMapper.selectList(queryWrapper); |
| | | } |
| | | |
| | |
| | | PurchaseLedger purchaseLedger = new PurchaseLedger(); |
| | | // DTO转Entity |
| | | BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger); |
| | | AmountUtils.normalizeContractAmount(purchaseLedger); |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(purchaseLedgerDto.getSalesLedgerId()); |
| | | //录入人 |
| | | SysUser sysUser = userMapper.selectUserById(purchaseLedgerDto.getRecorderId()); |
| | |
| | | purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId()); |
| | | |
| | | purchaseLedger.setApprovalStatus(1); |
| | | // 实际合同金额始终等于合同金额 |
| | | purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount()); |
| | | // 3. 新增或更新主表 |
| | | if (purchaseLedger.getId() == null) { |
| | | if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) { |
| | | purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate())); |
| | | } |
| | | purchaseLedgerMapper.insert(purchaseLedger); |
| | | } else { |
| | | // 删除采购审核,重新提交 |
| | |
| | | // 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, "批量推进采购步骤完成"); |
| | | } |
| | | |
| | | |
| | |
| | | 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()); |
| | | }); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // 设置字段 |
| | | for (SalesLedgerProduct product : products) { |
| | | AmountUtils.normalizeSalesLedgerProduct(product); |
| | | product.setSalesLedgerId(salesLedgerId); |
| | | |
| | | Long productId = product.getProductId(); |
| | |
| | | } |
| | | |
| | | // 计算总含税金额 |
| | | BigDecimal totalTaxInclusiveAmount = products.stream() |
| | | BigDecimal totalTaxInclusiveAmount = AmountUtils.scaleAmount(products.stream() |
| | | .map(SalesLedgerProduct::getTaxInclusiveTotalPrice) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add)); |
| | | |
| | | // 更新主表的总金额字段 |
| | | if (salesLedgerId != null) { |
| | |
| | | // 4. 转换 DTO |
| | | PurchaseLedgerDto resultDto = new PurchaseLedgerDto(); |
| | | BeanUtils.copyProperties(purchaseLedger, resultDto); |
| | | AmountUtils.normalizeContractAmount(resultDto); |
| | | if (!products.isEmpty()) { |
| | | AmountUtils.normalizeSalesLedgerProducts(products); |
| | | resultDto.setHasChildren(true); |
| | | resultDto.setProductData(products); |
| | | resultDto.setStorageBlobVOS(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId())); |
| | |
| | | queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()) |
| | | .eq(SalesLedgerProduct::getType, 2); |
| | | List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(queryWrapper); |
| | | AmountUtils.normalizeSalesLedgerProducts(productList); |
| | | resultDto.setProductData(productList); |
| | | return resultDto; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger) { |
| | | return purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger); |
| | | IPage<PurchaseLedgerDto> page = purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger); |
| | | if (!CollectionUtils.isEmpty(page.getRecords())) { |
| | | page.getRecords().forEach(record -> { |
| | | AmountUtils.normalizeContractAmount(record); |
| | | AmountUtils.normalizeSalesLedgerProducts(record.getProductData()); |
| | | }); |
| | | } |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public String getPurchaseNo(Date entryDate) { |
| | | LocalDate localDate = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | public String getPurchaseNo() { |
| | | // 生成日期前缀(例如:CG20250405) |
| | | String purchaseNo = "CG" + localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String purchaseNo = "CG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | |
| | | // 构建 Redis Key(按天分隔) |
| | | String redisKey = "purchase_no:" + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | String redisKey = "purchase_no:" + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | // 获取当前序号并递增(原子操作) |
| | | Long sequence = redisTemplate.opsForValue().increment(redisKey); |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public AjaxResult importData(MultipartFile file) { |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | try { |
| | | InputStream inputStream = file.getInputStream(); |
| | | ExcelUtil<PurchaseLedgerImportDto> salesLedgerImportDtoExcelUtil = new ExcelUtil<>(PurchaseLedgerImportDto.class); |
| | | Map<String, List<PurchaseLedgerImportDto>> stringListMap = salesLedgerImportDtoExcelUtil.importExcelMultiSheet(Arrays.asList("采购台账数据", "采购产品数据"), inputStream, 0); |
| | | if (CollectionUtils.isEmpty(stringListMap)) return AjaxResult.error("采购表格为空!"); |
| | | // 业务层合并 |
| | | List<PurchaseLedgerImportDto> salesLedgerImportDtoList = stringListMap.get("采购台账数据"); |
| | | if (CollectionUtils.isEmpty(salesLedgerImportDtoList)) return AjaxResult.error("采购台账数据为空!"); |
| | | List<PurchaseLedgerImportDto> salesLedgerProductImportDtoList = stringListMap.get("采购产品数据"); |
| | | if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return AjaxResult.error("采购产品数据为空!"); |
| | | // 供应商数据 |
| | | List<SupplierManage> customers = supplierManageMapper.selectList(new LambdaQueryWrapper<SupplierManage>().in(SupplierManage::getSupplierName, |
| | | salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getSupplierName).collect(Collectors.toList()))); |
| | | List<Map<String,Object>> list = productModelMapper.getProductAndModelList(); |
| | | // 录入人数据 |
| | | 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>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, salesLedgerImportDto.getPurchaseContractNumber()) |
| | | .last("limit 1")); |
| | | if(purchaseLedger != null){ |
| | | continue; |
| | | } |
| | | PurchaseLedger salesLedger = new PurchaseLedger(); |
| | | BeanUtils.copyProperties(salesLedgerImportDto, salesLedger); |
| | | // 通过供应商名称查询ID |
| | | salesLedger.setSupplierId(customers.stream() |
| | | .filter(customer -> customer.getSupplierName().equals(salesLedger.getSupplierName())) |
| | | .findFirst() |
| | | .map(SupplierManage::getId) |
| | | .orElse(null)); |
| | | Long aLong = sysUsers.stream() |
| | | .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getRecorderName())) |
| | | .findFirst() |
| | | .map(SysUser::getUserId) |
| | | .orElse(null); |
| | | if (aLong == null) |
| | | throw new RuntimeException("录入人:" + salesLedger.getRecorderName() + ",无对应用户!"); |
| | | salesLedger.setRecorderId(aLong); |
| | | // 采购产品数据绑定,通过采购单号获取对应采购产品数据 |
| | | List<PurchaseLedgerProductImportDto> salesLedgerProductImportDtos = salesLedgerProductImportDtoList.stream() |
| | | .filter(salesLedgerProductImportDto -> salesLedgerProductImportDto.getPurchaseContractNumber().equals(salesLedger.getPurchaseContractNumber())) |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isEmpty(salesLedgerProductImportDtos)) |
| | | throw new RuntimeException("采购单号:" + salesLedgerImportDto.getPurchaseContractNumber() + ",无对应产品数据!"); |
| | | salesLedger.setContractAmount(salesLedgerProductImportDtos.stream() |
| | | .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice) |
| | | .reduce(BigDecimal.ZERO,BigDecimal::add)); |
| | | salesLedger.setNetContractAmount(salesLedger.getContractAmount()); |
| | | // 通过销售单号绑定销售 |
| | | SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>() |
| | | .eq(SalesLedger::getSalesContractNo, salesLedger.getSalesContractNo()) |
| | | .last("LIMIT 1")); |
| | | if(salesLedger1 != null){ |
| | | salesLedger.setSalesLedgerId(salesLedger1.getId()); |
| | | } |
| | | if (StringUtils.hasText(salesLedger.getApproveUserIds())) { |
| | | // 采购审核:历史导入模板传审批人姓名时,继续兼容转换为用户ID。 |
| | | String[] split = salesLedger.getApproveUserIds().split(","); |
| | | List<Long> ids = new ArrayList<>(); |
| | | for (int i = 0; i < split.length; i++) { |
| | | SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getNickName, split[i]) |
| | | .last("LIMIT 1")); |
| | | if (sysUser != null) { |
| | | ids.add(sysUser.getUserId()); |
| | | } |
| | | } |
| | | // 将集合转为字符串,隔开 |
| | | String collect = ids.stream().map(Object::toString).collect(Collectors.joining(",")); |
| | | salesLedger.setApproveUserIds(collect); |
| | | } |
| | | purchaseLedgerMapper.insert(salesLedger); |
| | | |
| | | for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) { |
| | | SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct(); |
| | | BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct); |
| | | 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.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); |
| | | } |
| | | // 采购审核 |
| | | addApproveByPurchase(loginUser,salesLedger); |
| | | try (InputStream inputStream = file.getInputStream()) { |
| | | ExcelUtil<PurchaseLedgerImportDto> excelUtil = new ExcelUtil<>(PurchaseLedgerImportDto.class); |
| | | Map<String, List<PurchaseLedgerImportDto>> sheetData = excelUtil.importExcelMultiSheet( |
| | | Arrays.asList("采购台账数据", "采购产品数据"), inputStream, 0); |
| | | if (CollectionUtils.isEmpty(sheetData)) { |
| | | return AjaxResult.error("采购表格为空!"); |
| | | } |
| | | |
| | | return AjaxResult.success("导入成功"); |
| | | List<PurchaseLedgerImportDto> purchaseImports = sheetData.get("采购台账数据"); |
| | | if (CollectionUtils.isEmpty(purchaseImports)) { |
| | | return AjaxResult.error("采购台账数据为空!"); |
| | | } |
| | | List<PurchaseLedgerImportDto> productImports = sheetData.get("采购产品数据"); |
| | | if (CollectionUtils.isEmpty(productImports)) { |
| | | return AjaxResult.error("采购产品数据为空!"); |
| | | } |
| | | |
| | | List<String> supplierNames = purchaseImports.stream() |
| | | .map(PurchaseLedgerImportDto::getSupplierName) |
| | | .filter(StringUtils::hasText) |
| | | .map(StringUtils::trim) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | Map<String, SupplierManage> supplierMap = CollectionUtils.isEmpty(supplierNames) |
| | | ? Collections.emptyMap() |
| | | : supplierManageMapper.selectList(new LambdaQueryWrapper<SupplierManage>() |
| | | .in(SupplierManage::getSupplierName, supplierNames)) |
| | | .stream() |
| | | .collect(Collectors.toMap( |
| | | supplier -> StringUtils.trim(supplier.getSupplierName()), |
| | | supplier -> supplier, |
| | | (first, ignored) -> first)); |
| | | |
| | | List<String> recorderNames = purchaseImports.stream() |
| | | .map(PurchaseLedgerImportDto::getRecorderName) |
| | | .filter(StringUtils::hasText) |
| | | .map(StringUtils::trim) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | Map<String, SysUser> recorderMap = CollectionUtils.isEmpty(recorderNames) |
| | | ? Collections.emptyMap() |
| | | : sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>() |
| | | .in(SysUser::getNickName, recorderNames)) |
| | | .stream() |
| | | .collect(Collectors.toMap( |
| | | user -> StringUtils.trim(user.getNickName()), |
| | | user -> user, |
| | | (first, ignored) -> first)); |
| | | |
| | | Map<String, ProductModel> importedProductModelCache = new HashMap<>(); |
| | | int addedCount = 0; |
| | | int overwrittenCount = 0; |
| | | int skippedCount = 0; |
| | | |
| | | for (PurchaseLedgerImportDto purchaseImport : purchaseImports) { |
| | | String purchaseContractNumber = requireImportText( |
| | | purchaseImport.getPurchaseContractNumber(), "采购单号不能为空!"); |
| | | purchaseImport.setPurchaseContractNumber(purchaseContractNumber); |
| | | purchaseImport.setSupplierName(StringUtils.trim(purchaseImport.getSupplierName())); |
| | | purchaseImport.setProjectName(StringUtils.trim(purchaseImport.getProjectName())); |
| | | purchaseImport.setRecorderName(StringUtils.trim(purchaseImport.getRecorderName())); |
| | | |
| | | List<PurchaseLedgerProductImportDto> purchaseProducts = productImports.stream() |
| | | .filter(productImport -> sameImportText( |
| | | productImport.getPurchaseContractNumber(), purchaseContractNumber)) |
| | | .map(productImport -> (PurchaseLedgerProductImportDto) productImport) |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isEmpty(purchaseProducts)) { |
| | | throw new BaseException("采购单号:" + purchaseContractNumber + ",无对应产品数据!"); |
| | | } |
| | | |
| | | List<PurchaseLedger> existingLedgers = purchaseLedgerMapper.selectList( |
| | | new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, purchaseContractNumber)); |
| | | PurchaseLedger ledgerToOverwrite = existingLedgers.stream() |
| | | .filter(existing -> Objects.equals(existing.getApprovalStatus(), 1)) |
| | | .filter(existing -> isSameImportedPurchase(existing, purchaseImport)) |
| | | .findFirst() |
| | | .orElse(null); |
| | | if (ledgerToOverwrite == null && CollectionUtils.isNotEmpty(existingLedgers)) { |
| | | skippedCount++; |
| | | continue; |
| | | } |
| | | |
| | | PurchaseLedger purchaseLedger = buildImportedPurchaseLedger( |
| | | purchaseImport, purchaseProducts, supplierMap, recorderMap); |
| | | boolean overwrite = ledgerToOverwrite != null; |
| | | if (overwrite) { |
| | | purchaseLedger.setId(ledgerToOverwrite.getId()); |
| | | purchaseLedger.setApprovalStatus(1); |
| | | updateImportedPurchaseLedger(purchaseLedger); |
| | | clearImportedPurchaseContents(purchaseLedger.getId(), purchaseContractNumber); |
| | | overwrittenCount++; |
| | | } else { |
| | | purchaseLedger.setApprovalStatus(1); |
| | | purchaseLedgerMapper.insert(purchaseLedger); |
| | | addedCount++; |
| | | } |
| | | |
| | | for (PurchaseLedgerProductImportDto productImport : purchaseProducts) { |
| | | ProductModel productModel = findOrCreateImportedProductModel( |
| | | productImport, importedProductModelCache); |
| | | SalesLedgerProduct purchaseProduct = new SalesLedgerProduct(); |
| | | BeanUtils.copyProperties(productImport, purchaseProduct); |
| | | purchaseProduct.setSalesLedgerId(purchaseLedger.getId()); |
| | | purchaseProduct.setType(2); |
| | | purchaseProduct.setProductId(productModel.getProductId()); |
| | | purchaseProduct.setProductModelId(productModel.getId()); |
| | | AmountUtils.normalizeSalesLedgerProduct(purchaseProduct); |
| | | purchaseProduct.setTaxExclusiveTotalPrice( |
| | | AmountUtils.calcTaxExclusiveTotalPrice( |
| | | purchaseProduct.getTaxInclusiveTotalPrice(), |
| | | purchaseProduct.getTaxRate())); |
| | | purchaseProduct.setRegister(loginUser.getNickName()); |
| | | purchaseProduct.setRegisterDate(LocalDateTime.now()); |
| | | purchaseProduct.setApproveStatus(0); |
| | | purchaseProduct.setIsChecked(Objects.equals(productImport.getIsChecked(), 1)); |
| | | salesLedgerProductMapper.insert(purchaseProduct); |
| | | if (Boolean.TRUE.equals(purchaseProduct.getIsChecked())) { |
| | | addQualityInspect(purchaseLedger, purchaseProduct); |
| | | } |
| | | } |
| | | |
| | | if (!overwrite) { |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | } |
| | | } |
| | | |
| | | return AjaxResult.success(String.format( |
| | | "导入成功:新增%d条,覆盖%d条,跳过%d条", addedCount, overwrittenCount, skippedCount)); |
| | | } catch (BaseException e) { |
| | | log.error("导入采购台账失败:{}", e.getMessage(), e); |
| | | throw e; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | log.error("导入采购台账失败", e); |
| | | throw new BaseException("导入失败:" + e.getMessage()); |
| | | } |
| | | return AjaxResult.success("导入失败"); |
| | | } |
| | | |
| | | private PurchaseLedger buildImportedPurchaseLedger( |
| | | PurchaseLedgerImportDto purchaseImport, |
| | | List<PurchaseLedgerProductImportDto> purchaseProducts, |
| | | Map<String, SupplierManage> supplierMap, |
| | | Map<String, SysUser> recorderMap) { |
| | | PurchaseLedger purchaseLedger = new PurchaseLedger(); |
| | | BeanUtils.copyProperties(purchaseImport, purchaseLedger); |
| | | |
| | | SupplierManage supplier = supplierMap.get(purchaseLedger.getSupplierName()); |
| | | purchaseLedger.setSupplierId(supplier == null ? null : supplier.getId()); |
| | | |
| | | SysUser recorder = recorderMap.get(purchaseLedger.getRecorderName()); |
| | | if (recorder == null) { |
| | | throw new BaseException("录入人:" + purchaseLedger.getRecorderName() + ",无对应用户!"); |
| | | } |
| | | purchaseLedger.setRecorderId(recorder.getUserId()); |
| | | purchaseLedger.setContractAmount(purchaseProducts.stream() |
| | | .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add)); |
| | | |
| | | if (StringUtils.hasText(purchaseLedger.getSalesContractNo())) { |
| | | SalesLedger salesLedger = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>() |
| | | .eq(SalesLedger::getSalesContractNo, purchaseLedger.getSalesContractNo()) |
| | | .last("LIMIT 1")); |
| | | purchaseLedger.setSalesLedgerId(salesLedger == null ? null : salesLedger.getId()); |
| | | } else { |
| | | purchaseLedger.setSalesLedgerId(null); |
| | | } |
| | | purchaseLedger.setApproveUserIds(convertImportedApproverNames(purchaseLedger.getApproveUserIds())); |
| | | return purchaseLedger; |
| | | } |
| | | |
| | | private String convertImportedApproverNames(String approverNames) { |
| | | if (!StringUtils.hasText(approverNames)) { |
| | | return approverNames; |
| | | } |
| | | List<Long> userIds = new ArrayList<>(); |
| | | for (String approverName : approverNames.split("[,,]")) { |
| | | String trimmedName = StringUtils.trim(approverName); |
| | | if (!StringUtils.hasText(trimmedName)) { |
| | | continue; |
| | | } |
| | | SysUser user = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .eq(SysUser::getNickName, trimmedName) |
| | | .last("LIMIT 1")); |
| | | if (user != null) { |
| | | userIds.add(user.getUserId()); |
| | | } |
| | | } |
| | | return userIds.stream().map(String::valueOf).collect(Collectors.joining(",")); |
| | | } |
| | | |
| | | private ProductModel findOrCreateImportedProductModel( |
| | | PurchaseLedgerProductImportDto productImport, |
| | | Map<String, ProductModel> cache) { |
| | | String productCategory = requireImportText( |
| | | productImport.getProductCategory(), "采购产品的产品大类不能为空!"); |
| | | String specificationModel = StringUtils.trim(productImport.getSpecificationModel()); |
| | | if (!StringUtils.hasText(specificationModel)) { |
| | | specificationModel = productCategory; |
| | | } |
| | | productImport.setProductCategory(productCategory); |
| | | productImport.setSpecificationModel(specificationModel); |
| | | productImport.setUnit(StringUtils.trim(productImport.getUnit())); |
| | | |
| | | String cacheKey = productCategory + "::" + specificationModel; |
| | | ProductModel cachedModel = cache.get(cacheKey); |
| | | if (cachedModel != null) { |
| | | return cachedModel; |
| | | } |
| | | |
| | | List<Product> sameNameProducts = productMapper.selectList(new LambdaQueryWrapper<Product>() |
| | | .eq(Product::getProductName, productCategory)); |
| | | for (Product product : sameNameProducts) { |
| | | ProductModel existingModel = productModelMapper.selectOne(new LambdaQueryWrapper<ProductModel>() |
| | | .eq(ProductModel::getProductId, product.getId()) |
| | | .eq(ProductModel::getModel, specificationModel) |
| | | .last("LIMIT 1")); |
| | | if (existingModel != null) { |
| | | cache.put(cacheKey, existingModel); |
| | | return existingModel; |
| | | } |
| | | } |
| | | |
| | | Product product = sameNameProducts.stream() |
| | | .filter(item -> Objects.equals(item.getParentId(), IMPORT_PRODUCT_PARENT_ID)) |
| | | .findFirst() |
| | | .orElse(null); |
| | | if (product == null) { |
| | | product = new Product(); |
| | | product.setParentId(IMPORT_PRODUCT_PARENT_ID); |
| | | product.setProductName(productCategory); |
| | | productMapper.insert(product); |
| | | } |
| | | |
| | | ProductModel productModel = new ProductModel(); |
| | | productModel.setProductId(product.getId()); |
| | | productModel.setModel(specificationModel); |
| | | productModel.setUnit(productImport.getUnit()); |
| | | productModel.setProductCode(specificationModel + "-001"); |
| | | productModelMapper.insert(productModel); |
| | | cache.put(cacheKey, productModel); |
| | | return productModel; |
| | | } |
| | | |
| | | private void updateImportedPurchaseLedger(PurchaseLedger purchaseLedger) { |
| | | purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getId, purchaseLedger.getId()) |
| | | .set(PurchaseLedger::getPurchaseContractNumber, purchaseLedger.getPurchaseContractNumber()) |
| | | .set(PurchaseLedger::getSupplierId, purchaseLedger.getSupplierId()) |
| | | .set(PurchaseLedger::getSupplierName, purchaseLedger.getSupplierName()) |
| | | .set(PurchaseLedger::getRecorderId, purchaseLedger.getRecorderId()) |
| | | .set(PurchaseLedger::getRecorderName, purchaseLedger.getRecorderName()) |
| | | .set(PurchaseLedger::getSalesContractNo, purchaseLedger.getSalesContractNo()) |
| | | .set(PurchaseLedger::getProjectName, purchaseLedger.getProjectName()) |
| | | .set(PurchaseLedger::getEntryDate, purchaseLedger.getEntryDate()) |
| | | .set(PurchaseLedger::getExecutionDate, purchaseLedger.getExecutionDate()) |
| | | .set(PurchaseLedger::getRemarks, purchaseLedger.getRemarks()) |
| | | .set(PurchaseLedger::getSalesLedgerId, purchaseLedger.getSalesLedgerId()) |
| | | .set(PurchaseLedger::getContractAmount, purchaseLedger.getContractAmount()) |
| | | .set(PurchaseLedger::getPaymentMethod, purchaseLedger.getPaymentMethod()) |
| | | .set(PurchaseLedger::getApproveUserIds, purchaseLedger.getApproveUserIds()) |
| | | .set(PurchaseLedger::getApprovalStatus, 1) |
| | | .set(PurchaseLedger::getUpdatedAt, new Date())); |
| | | } |
| | | |
| | | private void clearImportedPurchaseContents(Long purchaseLedgerId, String purchaseContractNumber) { |
| | | List<QualityInspect> qualityInspects = qualityInspectMapper.selectList( |
| | | new LambdaQueryWrapper<QualityInspect>() |
| | | .eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId)); |
| | | boolean hasSubmittedInspect = qualityInspects.stream() |
| | | .anyMatch(inspect -> Objects.equals(inspect.getInspectState(), 1)); |
| | | if (hasSubmittedInspect) { |
| | | throw new BaseException("采购单号:" + purchaseContractNumber + ",存在已提交的检验单,不能覆盖!"); |
| | | } |
| | | List<Long> inspectIds = qualityInspects.stream() |
| | | .map(QualityInspect::getId) |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isNotEmpty(inspectIds)) { |
| | | qualityInspectParamMapper.delete(new LambdaQueryWrapper<QualityInspectParam>() |
| | | .in(QualityInspectParam::getInspectId, inspectIds)); |
| | | qualityInspectMapper.delete(new LambdaQueryWrapper<QualityInspect>() |
| | | .eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId)); |
| | | } |
| | | |
| | | List<SalesLedgerProduct> existingProducts = salesLedgerProductMapper.selectList( |
| | | new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedgerId) |
| | | .eq(SalesLedgerProduct::getType, 2)); |
| | | for (SalesLedgerProduct existingProduct : existingProducts) { |
| | | procurementRecordStorageMapper.delete(new LambdaQueryWrapper<ProcurementRecordStorage>() |
| | | .eq(ProcurementRecordStorage::getSalesLedgerProductId, existingProduct.getId())); |
| | | } |
| | | salesLedgerProductMapper.delete(new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedgerId) |
| | | .eq(SalesLedgerProduct::getType, 2)); |
| | | } |
| | | |
| | | private boolean isSameImportedPurchase( |
| | | PurchaseLedger existingLedger, PurchaseLedgerImportDto purchaseImport) { |
| | | return sameImportText(existingLedger.getPurchaseContractNumber(), purchaseImport.getPurchaseContractNumber()) |
| | | && sameImportText(existingLedger.getSupplierName(), purchaseImport.getSupplierName()) |
| | | && sameImportText(existingLedger.getProjectName(), purchaseImport.getProjectName()) |
| | | && sameImportDate(existingLedger.getExecutionDate(), purchaseImport.getExecutionDate()) |
| | | && sameImportDate(existingLedger.getEntryDate(), purchaseImport.getEntryDate()); |
| | | } |
| | | |
| | | private boolean sameImportText(String left, String right) { |
| | | return Objects.equals(StringUtils.trim(left), StringUtils.trim(right)); |
| | | } |
| | | |
| | | private boolean sameImportDate(Date left, Date right) { |
| | | if (left == null || right == null) { |
| | | return left == null && right == null; |
| | | } |
| | | LocalDate leftDate = Instant.ofEpochMilli(left.getTime()) |
| | | .atZone(ZoneId.systemDefault()) |
| | | .toLocalDate(); |
| | | LocalDate rightDate = Instant.ofEpochMilli(right.getTime()) |
| | | .atZone(ZoneId.systemDefault()) |
| | | .toLocalDate(); |
| | | return leftDate.equals(rightDate); |
| | | } |
| | | |
| | | private String requireImportText(String value, String message) { |
| | | String trimmedValue = StringUtils.trim(value); |
| | | if (!StringUtils.hasText(trimmedValue)) { |
| | | throw new BaseException(message); |
| | | } |
| | | return trimmedValue; |
| | | } |
| | | |
| | | @Override |
| | |
| | | // 4. 转换 DTO |
| | | PurchaseLedgerDto resultDto = new PurchaseLedgerDto(); |
| | | BeanUtils.copyProperties(purchaseLedger, resultDto); |
| | | AmountUtils.normalizeContractAmount(resultDto); |
| | | if (!products.isEmpty()) { |
| | | AmountUtils.normalizeSalesLedgerProducts(products); |
| | | resultDto.setHasChildren(true); |
| | | resultDto.setProductData(products); |
| | | } |
| | |
| | | if (loginUser == null) { |
| | | return; |
| | | } |
| | | 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); |
| | | |
| | | 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()); |
| | | approveProcessVO.setPrice(AmountUtils.scaleAmount(purchaseLedger.getContractAmount())); |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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 |
| | | ); |
| | | @Override |
| | | public Map<String, Object> getPrintData(Long id) { |
| | | PurchaseLedger purchaseLedger = getById(id); |
| | | if (purchaseLedger == null) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList( |
| | | new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, id) |
| | | .eq(SalesLedgerProduct::getType, 2) |
| | | ); |
| | | SysDept dept = sysDeptMapper.selectDeptById(100L); |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("companyName", dept != null ? dept.getDeptName() : ""); |
| | | result.put("ledger", purchaseLedger); |
| | | result.put("products", products); |
| | | return result; |
| | | } |
| | | } |