| | |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | 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.enums.TypeEnums; |
| | | 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; |
| | |
| | | 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); |
| | | } |
| | | |
| | | @Override |
| | | public List<PurchaseLedger> selectReturnablePurchaseLedgerList(PurchaseLedger purchaseLedger) { |
| | | return purchaseLedgerMapper.selectReturnablePurchaseLedgerList(purchaseLedger); |
| | | } |
| | | |
| | | @Override |
| | |
| | | purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId()); |
| | | |
| | | purchaseLedger.setApprovalStatus(1); |
| | | // 实际合同金额始终等于合同金额 |
| | | purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount()); |
| | | // 3. 新增或更新主表 |
| | | if (purchaseLedger.getId() == null) { |
| | | purchaseLedgerMapper.insert(purchaseLedger); |
| | | } else { |
| | | // 删除采购审核,重新提交 |
| | | ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveType, 5) |
| | | .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber()) |
| | | .eq(ApproveProcess::getApproveDelete, 0) |
| | | .last("limit 1")); |
| | | if (one != null) { |
| | | approveProcessService.delByIds(Collections.singletonList(one.getId())); |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | if (purchaseLedger.getId() == null && !StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) { |
| | | purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate())); |
| | | } |
| | | this.saveOrUpdate(purchaseLedger); |
| | | |
| | | // 4. 处理子表数据 |
| | | List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData(); |
| | |
| | | handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType()); |
| | | } |
| | | // 6.采购审核新增;审批管理未配置采购审批人时,审批服务会自动置为审批通过。 |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | //查询当前采购合同号是否已添加审批,如果有且不是驳回状态,则不重复添加 |
| | | ApprovalInstance oldApprovalInstance = approvalInstanceService.getOne(Wrappers.<ApprovalInstance>lambdaQuery() |
| | | .eq(ApprovalInstance::getDeleted, 0) |
| | | .eq(ApprovalInstance::getBusinessType, TypeEnums.PURCHASE_APPROVAL.getCode()) |
| | | .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId()) |
| | | .ne(ApprovalInstance::getStatus,"REJECTED") |
| | | .last("limit 1") |
| | | ); |
| | | if(Objects.isNull(oldApprovalInstance)){ |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | } |
| | | // 5. 迁移临时文件到正式目录 |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS()); |
| | | return 1; |
| | |
| | | for (Long id : ids) { |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id); |
| | | if(purchaseLedger != null){ |
| | | ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>() |
| | | List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>() |
| | | .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId()) |
| | | .eq(ApprovalInstance::getBusinessType, 5L) |
| | | .eq(ApprovalInstance::getDeleted, 0)); |
| | | if (CollectionUtils.isNotEmpty(approvalInstances)) { |
| | | approvalInstanceService.delete( |
| | | approvalInstances.stream() |
| | | .map(ApprovalInstance::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()) |
| | | ); |
| | | } |
| | | |
| | | List<ApproveProcess> approveProcesses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveType, 5) |
| | | .eq(ApproveProcess::getApproveDelete, 0) |
| | | .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber()) |
| | | .last("limit 1")); |
| | | if (one != null) { |
| | | approveProcessService.delByIds(Collections.singletonList(one.getId())); |
| | | .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber())); |
| | | if (CollectionUtils.isNotEmpty(approveProcesses)) { |
| | | approveProcessService.delByIds( |
| | | approveProcesses.stream() |
| | | .map(ApproveProcess::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()) |
| | | ); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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()) |
| | |
| | | return resultDto; |
| | | } |
| | | |
| | | public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) throws Exception { |
| | | public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) { |
| | | if (loginUser == null) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | 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; |
| | | } |
| | | } |