package cn.palmte.work.service; import org.springframework.beans.BeanUtils; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; import cn.palmte.work.ErrorMessageException; import cn.palmte.work.config.activiti.ActConstant; import cn.palmte.work.config.activiti.ActProjectTypeEnum; import cn.palmte.work.model.Admin; import cn.palmte.work.model.AdminRepository; import cn.palmte.work.model.Dept; import cn.palmte.work.model.DeptRepository; import cn.palmte.work.model.ProcurementType; import cn.palmte.work.model.Project; import cn.palmte.work.model.ProjectBudgetCostDetail; import cn.palmte.work.model.ProjectRepository; import cn.palmte.work.model.enums.ProcessStatus; import cn.palmte.work.model.enums.ProcessType; import cn.palmte.work.model.process.BudgetPurchaseAmount; import cn.palmte.work.model.process.BudgetPurchaseDetail; import cn.palmte.work.model.process.ProcurementContract; import cn.palmte.work.model.process.ProcurementDetail; import cn.palmte.work.model.process.ProjectProcess; import cn.palmte.work.model.process.QueryProject; import cn.palmte.work.model.process.SaleContract; import cn.palmte.work.model.process.SupplierMaterial; import cn.palmte.work.model.process.form.SaleContractDetailForm; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; /** * @author Harry Yang * @since 2.0 2022/12/23 09:39 */ @Service @RequiredArgsConstructor public class ProjectProcessService { private final JdbcTemplate jdbcTemplate; private final EntityManager entityManager; private final ProjectService projectService; private final ProjectInstanceService projectInstanceService; private final DeptRepository deptRepository; private final AdminRepository userRepository; private final ProjectRepository projectRepository; public List queryProjects(String q, ProcessType processType, Admin loginUser) { List projects = Collections.emptyList(); if (processType == ProcessType.procurement_contract) { projects = projectRepository.findBudgetPassedProjects(q); } else if (processType == ProcessType.sale_contract) { List queryProjects = jdbcTemplate.query( "select distinct p.id, p.name, p.project_no projectNo, pp.process_type processType,p.creator_id creatorId " + " from project p left join project_process pp on p.id = pp.project_id " + "where (p.`status` > 5 or (p.`status` = 5 and p.approve_status_budget = 2)) " + " and (p.`project_no` like concat('%', ?, '%') or p.`name` like concat('%', ?, '%'))", new Object[] { q, q }, BeanPropertyRowMapper.newInstance(QueryProject.class)); MultiValueMap idMap = new LinkedMultiValueMap<>(); for (QueryProject queryProject : queryProjects) { idMap.computeIfAbsent(queryProject.getId(), s -> new ArrayList<>()) .add(queryProject); } projects = queryProjects.stream().filter(queryProject -> { List projectList = idMap.get(queryProject.getId()); return projectList.stream().noneMatch(this::isSaleContract); }).map(queryProject -> { Project project = new Project(); project.setId(queryProject.getId()); project.setName(queryProject.getName()); project.setProjectNo(queryProject.getProjectNo()); project.setCreatorId(queryProject.getCreatorId()); return project; }).collect(Collectors.toList()); } return projectService.visibleProjects(projects, loginUser); } private boolean isSaleContract(QueryProject queryProject) { return queryProject.getProcessType() == ProcessType.sale_contract; } public BigDecimal getContractAmount(int id) { Map map = jdbcTemplate.queryForMap( "select ifnull(sum(amount * price), 0) contractAmount from project_budget_income_detail where project_id = ?", id); return (BigDecimal) map.values().iterator().next(); } @Data static class DeptReturnValue { private int id; private int leaderId; private String leaderName; private String name; private List children; public void addChildren(DeptReturnValue child) { if (children == null) { children = new ArrayList<>(); } children.add(child); } } public List filterDept() { List deptList = deptRepository.findEnable(); List level1 = filterByLevel(deptList, 1); List level2 = filterByLevel(deptList, 2); List level3 = filterByLevel(deptList, 3); List returnValues = new ArrayList<>(); for (Dept dept : level1) { // 只需要一级领导 Admin leader = userRepository.getAdminById(dept.getManagerId()); Integer leaderId = leader.getId(); String leaderName = leader.getRealName(); DeptReturnValue returnValue = createReturnValue(dept, leaderId, leaderName); for (Dept dept2 : level2) { if (Objects.equals(dept2.getParentId(), dept.getId())) { DeptReturnValue returnValue2 = createReturnValue(dept2, leaderId, leaderName); for (Dept dept3 : level3) { if (Objects.equals(dept3.getParentId(), dept2.getId())) { returnValue2.addChildren(createReturnValue(dept3, leaderId, leaderName)); } } returnValue.addChildren(returnValue2); } } returnValues.add(returnValue); } return returnValues; } private static List filterByLevel(List deptList, int level) { ArrayList ret = new ArrayList<>(); for (Dept dept : deptList) { if (Objects.equals(dept.getLevel(), level)) { ret.add(dept); } } return ret; } private DeptReturnValue createReturnValue(Dept dept, Integer leaderId, String leaderName) { DeptReturnValue returnValue = new DeptReturnValue(); returnValue.setId(dept.getId()); returnValue.setName(dept.getName()); returnValue.setLeaderId(leaderId); returnValue.setLeaderName(leaderName); return returnValue; } /** * 更新流程 审批人,和状态 * * @param processId 流程Id * @param auditId 审批人ID 可以为空 * @param status 流程状态 可以为空,为空的时候不修改 */ public void updateAudit(int processId, ProcessStatus status, List auditId) { if (CollectionUtils.isEmpty(auditId)) { if (status != null) { jdbcTemplate.update("update project_process set current_audit=?,current_audit_id=?,status=? where id=?", null, null, status.getValue(), processId); } else { jdbcTemplate.update("update project_process set current_audit=?,current_audit_id=? where id=?", null, null, processId); } } else { String currentAudit = getCurrentAudit(auditId); String currentAuditId = auditId.stream().map(String::valueOf).collect(Collectors.joining(",")); if (status != null) { jdbcTemplate.update("update project_process set current_audit=?,current_audit_id=?,status=? where id=?", currentAudit, currentAuditId, status.getValue(), processId); } else { jdbcTemplate.update("update project_process set current_audit=?,current_audit_id=? where id=?", currentAudit, currentAuditId, processId); } } } private String getCurrentAudit(List auditId) { TypedQuery query = entityManager.createQuery("from Admin where id in (:ids)", Admin.class); query.setParameter("ids", auditId); List resultList = query.getResultList(); return resultList.stream().map(Admin::getRealName) .collect(Collectors.joining(",")); } /** * 只更新状态 */ public void updateProcessStatus(int processId, ProcessStatus status) { jdbcTemplate.update("update project_process set `status`=? where id=?", status.getValue(), processId); } /** * 撤回流程 * * @param processId 撤回的ID */ @Transactional public void revoke(int processId) { jdbcTemplate.update("update project_process set current_audit=?, current_audit_id=?, `status`=? where id=?", null, null, ProcessStatus.draft.getValue(), processId); ProjectProcess process = obtain(processId); if (process.getProcessType() == ProcessType.procurement_contract) { // 撤回的时候要删除对应的采购数量 List purchaseAmount = getProcessPurchaseAmount(processId); for (BudgetPurchaseAmount amount : purchaseAmount) { amount.setSubmit(false); entityManager.merge(amount); } } } /** * 根据流程ID查询销售合同 * * @param processId 流程ID * @return 销售合同 */ public SaleContract findSaleContract(int processId) { TypedQuery query = entityManager.createQuery( "from SaleContract where processId=:processId", SaleContract.class); query.setParameter("processId", processId); return query.getSingleResult(); } /** * 根据流程ID查询采购合同 * * @param processId 流程ID * @return 采购合同 */ public ProcurementContract findProcurementContract(int processId) { TypedQuery query = entityManager.createQuery( "from ProcurementContract where processId=:processId", ProcurementContract.class); query.setParameter("processId", processId); return query.getSingleResult(); } public List getSupplierMaterials(int processId) { TypedQuery query = entityManager.createQuery( "from SupplierMaterial where processId=:processId", SupplierMaterial.class); query.setParameter("processId", processId); return query.getResultList(); } /** * 获取 采购清单 * * @param projectId 项目ID * @param processId 流程ID 一般用于获取详情 */ public List getProcurementDetails(int projectId, Integer processId) { ArrayList ret = new ArrayList<>(); if (processId != null) { // 根据流程获取 List purchaseAmount = getProcessPurchaseAmount(processId); for (BudgetPurchaseAmount amount : purchaseAmount) { ProjectBudgetCostDetail costDetail = getCostDetailById(amount.getBudgetCostId()); ProcurementDetail detail = new ProcurementDetail(); BeanUtils.copyProperties(costDetail, detail, "isUnderwritten"); Integer amountId = amount.getId(); // 找对应 预算采购明细的数量记录 的采购详情 List purchaseDetails = getBudgetPurchaseDetails(amountId); detail.setPurchaseDetails(purchaseDetails); detail.setBudgetCostId(costDetail.getId()); detail.setCategory(getCategory(costDetail)); detail.setAmountId(amountId); BigDecimal amountAlready = getAmountAlready(amount.getBudgetCostId()); detail.setAmountAlready(amountAlready); BigDecimal allAmount = amount.getAmount(); if (allAmount == null) { allAmount = costDetail.getAmount(); } detail.setAmountLeft(allAmount.subtract(amountAlready)); detail.setAmountCurrent(amount.getAmountCurrent()); detail.setIsUnderwritten(isProjectPrepaid(projectId)); ret.add(detail); } } else { List costDetails = getCostDetails(projectId); for (ProjectBudgetCostDetail costDetail : costDetails) { ProcurementDetail detail = new ProcurementDetail(); BeanUtils.copyProperties(costDetail, detail, "isUnderwritten"); // 可能为 0 BigDecimal amountAlready = getAmountAlready(costDetail.getId()); detail.setAmountAlready(amountAlready); BigDecimal allAmount = costDetail.getAmount(); detail.setAmountLeft(allAmount.subtract(amountAlready)); detail.setAmount(allAmount); // TODO 查询太频繁 detail.setCategory(getCategory(costDetail)); detail.setBudgetCostId(costDetail.getId()); detail.setIsUnderwritten(isProjectPrepaid(projectId)); ret.add(detail); } } return ret; } private String getCategory(ProjectBudgetCostDetail costDetail) { int category = costDetail.getCategory(); TypedQuery query = entityManager.createQuery( "from ProcurementType where id=:category and isDeleted=0 and enabled=1", ProcurementType.class); query.setParameter("category", category); try { ProcurementType procurementType = query.getSingleResult(); return procurementType.getName(); } catch (NoResultException e) { return String.valueOf(category); } } private List getBudgetPurchaseDetails(int amountId) { TypedQuery query = entityManager.createQuery( "from BudgetPurchaseDetail where amountId=:amountId", BudgetPurchaseDetail.class); query.setParameter("amountId", amountId); return query.getResultList(); } private List getBudgetPurchaseDetailsByProcessId(int processId) { TypedQuery query = entityManager.createQuery( "from BudgetPurchaseDetail where processId=:processId", BudgetPurchaseDetail.class); query.setParameter("processId", processId); return query.getResultList(); } private ProjectBudgetCostDetail getCostDetailById(int budgetCostId) { return entityManager.find(ProjectBudgetCostDetail.class, budgetCostId); } private List getCostDetails(int projectId) { TypedQuery query = entityManager.createQuery( "from ProjectBudgetCostDetail where projectId=:projectId", ProjectBudgetCostDetail.class); query.setParameter("projectId", projectId); return query.getResultList(); } /** * 获取采购合同流程所有的 预算采购明细的数量记录 * * @param processId 合同流程 * @return 采购合同流程预算采购明细的数量记录 列表 */ public List getProcessPurchaseAmount(Integer processId) { TypedQuery amountQuery = entityManager.createQuery( "from BudgetPurchaseAmount where processId=:processId", BudgetPurchaseAmount.class); amountQuery.setParameter("processId", processId); try { return amountQuery.getResultList(); } catch (NoResultException e) { return new ArrayList<>(); } } public BudgetPurchaseAmount getPurchaseAmount(int budgetCostId, Integer processId) { TypedQuery amountQuery = entityManager.createQuery( "from BudgetPurchaseAmount where budgetCostId=:budgetCostId and processId=:processId", BudgetPurchaseAmount.class); amountQuery.setParameter("budgetCostId", budgetCostId); amountQuery.setParameter("processId", processId); try { return amountQuery.getSingleResult(); } catch (NoResultException e) { return null; } } public List getPurchaseAmountList(int budgetCostId) { TypedQuery amountQuery = entityManager.createQuery( "from BudgetPurchaseAmount where budgetCostId=:budgetCostId", BudgetPurchaseAmount.class); amountQuery.setParameter("budgetCostId", budgetCostId); return amountQuery.getResultList(); } public List getProcessPurchaseDetail(Integer processId) { TypedQuery amountQuery = entityManager.createQuery( "from BudgetPurchaseDetail where processId=:processId", BudgetPurchaseDetail.class); amountQuery.setParameter("processId", processId); try { return amountQuery.getResultList(); } catch (NoResultException e) { return new ArrayList<>(); } } /** * 计算当前已经提交的采购数量 */ public BigDecimal getAmountAlready(int budgetCostId) { return getPurchaseAmountList(budgetCostId).stream() .filter(BudgetPurchaseAmount::isSubmitted) .map(BudgetPurchaseAmount::getAmountCurrent) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } public ProjectProcess getById(int id) { return entityManager.find(ProjectProcess.class, id); } public ProjectProcess obtain(int id) { ProjectProcess projectProcess = getById(id); if (projectProcess == null) { throw ErrorMessageException.failed("流程不存在"); } return projectProcess; } /** * 更新 销售合同 的 质保期 */ public void updateIncomeDetails(List incomeDetails) { if (!CollectionUtils.isEmpty(incomeDetails)) { jdbcTemplate.batchUpdate("update project_budget_income_detail set expiration_date =? where id =? ", new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { SaleContractDetailForm detailForm = incomeDetails.get(i); ps.setString(1, detailForm.getExpirationDate()); ps.setInt(2, detailForm.getId()); } @Override public int getBatchSize() { return incomeDetails.size(); } }); } } public BigDecimal getProjectRepaidAmount(int projectId) { Map map = jdbcTemplate.queryForMap( "select ifnull(sum(underwritten_plan), 0) repaidAmount from project_budget_plan_detail where project_id = ?", projectId); return (BigDecimal) map.values().iterator().next(); } /** * 发起流程审批 */ public void startAuditProgress(ProjectProcess entity) { HashMap variables = new HashMap<>(); Project project = projectRepository.findById(entity.getProjectId()); // 是否垫资 variables.put("isPrepaid", isProjectPrepaid(project)); // 垫资金额 BigDecimal repaidAmount = getProjectRepaidAmount(entity.getProjectId()); variables.put("repaidAmount", repaidAmount); if (entity.getProcessType() == ProcessType.procurement_contract) { // 合同金额 List purchaseDetails = getBudgetPurchaseDetailsByProcessId(entity.getId()); BigDecimal contractAmount = ProjectProcess.getContractAmountByPurchaseDetail(purchaseDetails); variables.put("contractAmount", contractAmount); } else { // 合同金额 variables.put("contractAmount", project.getContractAmount() == null ? 0 : project.getContractAmount()); } // 项目类型 variables.put("projectType", project.getType()); // 合作类型 variables.put("cooperationType", project.getCooperateType() == null ? 0 : project.getCooperateType()); // 部门领导ID variables.put(ActConstant.DEPT_LEADER_ID, entity.getApplyDeptLeaderId()); startAuditProgress(entity, variables); } /** * 是否垫资 */ public boolean isProjectPrepaid(Project project) { return isProjectPrepaid(project.getId()); } /** * 是否垫资 */ public boolean isProjectPrepaid(Integer projectId) { return isProjectPrepaid(getProjectRepaidAmount(projectId)); } /** * 是否垫资 */ public boolean isProjectPrepaid(BigDecimal repaidAmount) { return BigDecimal.ZERO.compareTo(repaidAmount) < 0; } /** * 发起流程审批 */ @SneakyThrows public void startAuditProgress(ProjectProcess entity, Map variables) { if (entity.getStatus() == ProcessStatus.to_be_audit) { switch (entity.getProcessType()) { case sale_contract: projectInstanceService.startProcessByProjectId(entity.getId(), ActProjectTypeEnum.SALE_CONTRACT, variables); break; case procurement_contract: projectInstanceService.startProcessByProjectId(entity.getId(), ActProjectTypeEnum.BUSINESS_PURCHASE, variables); break; default: throw new UnsupportedOperationException("还不支持"); } } } }