package com.ecep.contract.task; import com.ecep.contract.*; import com.ecep.contract.controller.project.cost.ProjectCostImportItemsFromContractsTasker; import com.ecep.contract.model.ContractFileTypeLocal; import com.ecep.contract.service.*; import com.ecep.contract.util.BeanContext; import com.ecep.contract.vo.*; import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.ObservableMap; import javafx.util.converter.NumberStringConverter; import lombok.Data; import org.springframework.beans.BeansException; import org.springframework.util.StringUtils; import java.io.File; import java.text.NumberFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; import java.util.stream.Collectors; @Data public class ContractVerifyComm implements BeanContext { BeanContext parent; public ContractVerifyComm(BeanContext parent) { this.parent = parent; } @Override public T getBean(Class requiredType) throws BeansException { return parent.getBean(requiredType); } @Override public T getCachedBean(Class requiredType) throws BeansException { return parent.getCachedBean(requiredType); } private ProjectService getProjectService() { return getCachedBean(ProjectService.class); } private ProjectSaleTypeService getSaleTypeService() { return getCachedBean(ProjectSaleTypeService.class); } private ProjectCostService getProjectCostService() { return getCachedBean(ProjectCostService.class); } private ProjectQuotationService getProjectQuotationService() { return getCachedBean(ProjectQuotationService.class); } private ProjectBidService getProjectBidService() { return getCachedBean(ProjectBidService.class); } private ProjectSaleTypeRequireFileTypeService getSaleTypeRequireFileTypeService() { return getCachedBean(ProjectSaleTypeRequireFileTypeService.class); } public ContractService getContractService() { return getCachedBean(ContractService.class); } private ContractFileService getContractFileService() { return getCachedBean(ContractFileService.class); } private ContractFileTypeService getContractFileTypeService() { return getCachedBean(ContractFileTypeService.class); } private ContractBidVendorService getContractBidVendorService() { return getCachedBean(ContractBidVendorService.class); } private CompanyService getCompanyService() { return getBean(CompanyService.class); } private CompanyFileService getCompanyFileService() { return getCachedBean(CompanyFileService.class); } private VendorGroupService getVendorGroupService() { return getCachedBean(VendorGroupService.class); } private VendorGroupRequireFileTypeService getVendorGroupRequireFileTypeService() { return getCachedBean(VendorGroupRequireFileTypeService.class); } private ExtendVendorInfoService getExtendVendorInfoService() { return getCachedBean(ExtendVendorInfoService.class); } private VendorService getVendorService() { return getCachedBean(VendorService.class); } private CustomerService getCompanyCustomerService() { return getCachedBean(CustomerService.class); } private CompanyCustomerFileService getCompanyCustomerFileService() { return getCachedBean(CompanyCustomerFileService.class); } private CompanyExtendInfoService getCompanyExtendInfoService() { return getCachedBean(CompanyExtendInfoService.class); } private EmployeeService getEmployeeService() { return getCachedBean(EmployeeService.class); } /** * */ private ObservableMap fileTypeLocalMap = null; private Locale locale = Locale.getDefault(); /** * 是否验证企业存储目录 */ private SimpleBooleanProperty verifyCompanyPath = new SimpleBooleanProperty(true); /** * 是否验证企业状态 */ private SimpleBooleanProperty verifyCompanyStatus = new SimpleBooleanProperty(true); /** * 是否验证企业文件(资信报告) */ private SimpleBooleanProperty verifyCompanyCredit = new SimpleBooleanProperty(true); /** * 是否验证客户 */ private SimpleBooleanProperty verifyCustomer = new SimpleBooleanProperty(true); /** * 是否验证客户文件(合同文件) */ private SimpleBooleanProperty verifyCustomerFiles = new SimpleBooleanProperty(true); /** * 是否验证客户子合同日期 */ private SimpleBooleanProperty verifyCustomerSubContractDate = new SimpleBooleanProperty(true); /** * 是否验证供应商 */ private SimpleBooleanProperty verifyVendor = new SimpleBooleanProperty(true); /** * 是否验证供应商文件(供应商合同) */ private SimpleBooleanProperty verifyVendorFiles = new SimpleBooleanProperty(true); /** * 合同合规性检测 * * @param contract 要验证的合同对象 * @param holder 输出 */ public void verify(ContractVo contract, MessageHolder holder) { LocalDate setupDate = contract.getSetupDate(); if (setupDate == null) { holder.error("未设置合同提交日期"); return; } Integer companyId = contract.getCompanyId(); if (companyId == null) { holder.error("未关联企业"); return; } CompanyVo company = getCompanyService().findById(companyId); if (company == null) { holder.error("合同关联公司 #" + companyId + " 异常,无法读取"); return; } company = getCompanyService().findById(company.getId()); verify(company, contract, holder); } public void verify(CompanyVo company, ContractVo contract, MessageHolder holder) { LocalDate setupDate = contract.getSetupDate(); Integer handlerId = contract.getHandlerId(); if (handlerId == null) { handlerId = contract.getEmployeeId(); } if (handlerId == null) { holder.error("未关联处理人"); return; } EmployeeVo employee = getEmployeeService().findById(handlerId); if (employee == null) { holder.error("关联处理人 #" + handlerId + " 异常,无法读取"); return; } if (contract.getAmount() == null || contract.getAmount() <= 0) { holder.error("合同金额小于等于0"); } CompanyExtendInfoVo companyExtendInfo = getCompanyExtendInfoService().findByCompany(company); if (companyExtendInfo == null) { CompanyExtendInfoVo extendInfo = new CompanyExtendInfoVo(); extendInfo.setCompanyId(company.getId()); extendInfo.setDisableVerify(false); companyExtendInfo = getCompanyExtendInfoService().save(extendInfo); } if (companyExtendInfo.isDisableVerify()) { holder.debug("公司设定不做校验"); } else { if (verifyCompanyPath.get()) { if (!getCompanyService().existsCompanyPath(company)) { holder.error("公司目录未设置"); } else { if (!getCompanyService().checkCompanyPathInBasePath(company)) { holder.error("公司目录未在规定目录下"); } } } if (verifyCompanyStatus.get()) { getCompanyService().verifyEnterpriseStatus(company, setupDate, holder); } if (verifyCompanyCredit.get()) { getCompanyFileService().verify(company, contract.getSetupDate(), holder); } } // 合同类型 switch (contract.getPayWay()) { case RECEIVE -> { // 销售合同 verifyAsCustomer(company, companyExtendInfo, contract, holder); // 销售合同下的采购合同 List list = getContractService().findAllBySaleContract(contract); if (!list.isEmpty()) { for (ContractVo v : list) { MessageHolder subHolder = holder.sub(v.getCode() + " -> "); subHolder.info("采购合同 : " + v.getName()); verify(v, subHolder); if (getContractService().existsContractPath(v)) { if (!getContractService().checkContractPathInBasePath(v)) { holder.error("合同目录未在规定目录下"); } } else { holder.error("合同目录未设置"); } } DoubleSummaryStatistics statistics = list.stream().mapToDouble(c -> { return Objects.requireNonNullElse(c.getAmount(), 0.0); }).summaryStatistics(); NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale); holder.debug("采购合同金额合计:" + numberFormat.format(statistics.getSum())); holder.debug("采购合同金额平均值:" + numberFormat.format(statistics.getAverage())); holder.debug("采购合同金额最大值:" + numberFormat.format(statistics.getMax())); holder.debug("采购合同金额最小值:" + numberFormat.format(statistics.getMin())); if (contract.getAmount() != null && statistics.getSum() > contract.getAmount()) { holder.warn("采购合同金额合计大于销售合同金额"); } } break; } case PAY -> { verifyAsVendor(company, contract, holder); break; } case OTHER -> { holder.error("合同付款类型:其他"); break; } default -> { holder.error("合同付款类型:未设置"); } } } private void verifyAsVendor(CompanyVo company, ContractVo contract, MessageHolder holder) { // 供应商,检查评价表 ExtendVendorInfoVo vendorInfo = getExtendVendorInfoService().findByContract(contract); if (vendorInfo == null) { ExtendVendorInfoVo info = getExtendVendorInfoService().newInstanceByContract(contract); vendorInfo = getExtendVendorInfoService().save(info); holder.info("创建供应商信息 #" + vendorInfo.getId()); } VendorGroupVo group = null; if (vendorInfo.getGroupId() != null) { group = getVendorGroupService().findById(vendorInfo.getGroupId()); } boolean assignedProvider = vendorInfo.isAssignedProvider(); if (assignedProvider) { holder.debug("采购信息中设定为指定供应商"); } if (verifyCustomerSubContractDate.get()) { // 检查子合同日期是否在销售合同之后 if (!vendorInfo.isPrePurchase()) { String parentCode = contract.getParentCode(); if (StringUtils.hasText(parentCode)) { ContractVo parent = getContractService().findByCode(parentCode); if (parent != null) { if (contract.getSetupDate().isBefore(parent.getSetupDate())) { holder.warn("采购合同:" + contract.getCode() + " 提交日期在销售合同之前"); } } } } } if (verifyVendor.get()) { getVendorService().verify(contract, holder); } if (verifyVendorFiles.get()) { holder.debug("核验文件..."); verifyVendorFile(group, assignedProvider, contract, holder); } } private void verifyVendorFile(VendorGroupVo group, boolean assignedProvider, ContractVo contract, MessageHolder holder) { if (group == null) { return; } boolean loseFile = false; ContractFileService fileService = getContractFileService(); List files = fileService.findAllByContract(contract); List list = getVendorGroupRequireFileTypeService().findByGroupId(group.getId()); if (list != null) { for (VendorGroupRequireFileTypeVo item : list) { ContractFileType fileType = item.getFileType(); if (fileType == null) { continue; } if (fileType == ContractFileType.QuotationSheet && assignedProvider && group.isRequireQuotationSheetForBid()) { continue; } if (!hasFileType(files, fileType)) { holder.error("供应商" + getFileTypeLocalValue(fileType) + "未上传"); loseFile = true; } } } // 供应商比价 if (group.isPriceComparison()) { if (assignedProvider) { holder.debug("指定供应商, 跳过供应商比价"); } else { boolean requireQuotation = group.isRequireQuotationSheetForBid(); List bidVendors = getContractBidVendorService().findByContract(contract); if (bidVendors == null || bidVendors.isEmpty()) { holder.error("未上报供应商比价"); } else { for (ContractBidVendorVo bidVendor : bidVendors) { if (bidVendor.getCompanyId() == null) { holder.warn("供应商比价:#" + bidVendor.getId() + " 未关联供应商"); continue; } CompanyVo company = getCompanyService().findById(bidVendor.getCompanyId()); ContractFileVo contractFile = fileService.findById(bidVendor.getQuotationSheetFileId()); // 报价表文件不存在 if (contractFile == null) { if (requireQuotation && bidVendor.getCompanyId().equals(contract.getCompanyId())) { holder.debug("供应商类型启用了允许选中供应商不必须要有报价表"); } else { holder.error("供应商比价:" + company.getName() + " 未上传/关联报价表"); loseFile = true; } } else { ContractFileType type = contractFile.getType(); if (type == null) { continue; } if (type != ContractFileType.QuotationSheet) { holder.error("供应商比价:" + contractFile.getFileName() + " 报价表记录异常,类型错误"); loseFile = true; } else { if (StringUtils.hasText(contractFile.getFileName())) { File file = new File(contract.getPath(), contractFile.getFileName()); if (!file.exists()) { holder.error("供应商比价:" + file.getName() + " 报价表记录异常,文件不存在"); loseFile = true; } } else { holder.error("供应商比价:" + company.getName() + " 报价表记录异常"); loseFile = true; } } } } } } } if (loseFile && files.isEmpty()) { holder.warn("!上传文件空!"); } } ContractFileTypeLocalVo getFileTypeLocal(ContractFileType type) { return getContractFileTypeService().findByType(getLocale(), type); } String getFileTypeLocalValue(ContractFileType type) { ContractFileTypeLocalVo fileTypeLocal = getFileTypeLocal(type); if (fileTypeLocal == null) { return type.name(); } return fileTypeLocal.getValue(); } private boolean hasFileType(List files, ContractFileType fileType) { if (files == null || files.isEmpty()) { return false; } for (ContractFileVo file : files) { if (file.getType() == fileType) { return true; } } return false; } private void verifyAsCustomer(CompanyVo company, CompanyExtendInfoVo companyExtendInfo, ContractVo contract, MessageHolder holder) { boolean valiad = true; ProjectVo project = null; Integer projectId = contract.getProject(); if (projectId == null) { // 收款的合同时,检查是否关联了项目,如果没有则创建 if (contract.getPayWay() == ContractPayWay.RECEIVE) { project = getProjectService().findByCode(contract.getCode()); if (project == null) { holder.info("创建关联项目"); try { project = getProjectService().newInstanceByContract(contract); project = getProjectService().save(project); } catch (Exception e) { holder.error("创建关联项目失败: " + e.getMessage()); throw new RuntimeException("code=" + contract.getCode(), e); } } contract.setProject(project.getId()); contract = getContractService().save(contract); } } else { project = getProjectService().findById(projectId); } if (project != null) { holder.info("验证项目信息:" + project.getCode() + " " + project.getName()); verifyProject(contract, project, holder.sub("项目")); Integer saleTypeId = project.getSaleTypeId(); if (saleTypeId != null) { ProjectSaleTypeVo saleType = getSaleTypeService().findById(saleTypeId); if (saleType != null) { if (getContractService().existsContractPath(contract)) { saleType = getSaleTypeService().findById(saleType.getId()); project.setSaleTypeId(saleType.getId()); if (!contract.getPath().startsWith(saleType.getPath())) { holder.error("合同目录未在销售类型目录下"); } } } } } if (!companyExtendInfo.isDisableVerify()) { if (!verifyCustomerContract(contract, holder)) { valiad = false; } if (verifyCustomerFiles.get()) { holder.debug("核验文件..."); if (!verifyContractFileAsCustomer(project, contract, holder)) { valiad = false; } } } } /** * 客户,检查评估表 */ private boolean verifyCustomerContract(ContractVo contract, MessageHolder holder) { if (!verifyCustomer.get()) { return false; } boolean valid = true; Integer companyId = contract.getCompanyId(); CompanyVo company = getCompanyService().findById(companyId); if (company == null) { holder.warn("合同未关联公司"); valid = false; } CustomerVo companyCustomer = getCompanyCustomerService().findByCompany(company); if (companyCustomer == null) { holder.warn("合同未关联客户"); valid = false; } else { if (!StringUtils.hasText(companyCustomer.getPath())) { holder.warn("客户未设置文件夹"); valid = false; } LocalDate developDate = companyCustomer.getDevelopDate(); if (developDate == null) { holder.warn("客户未设置开发日期"); valid = false; } if (!verifyCustomerFileByContract(companyCustomer, contract, holder)) { valid = false; } } return valid; } private boolean verifyCustomerFileByContract(CustomerVo companyCustomer, ContractVo contract, MessageHolder holder) { List verifyDates = new ArrayList<>(); LocalDate minDate = LocalDate.of(2022, 1, 1); LocalDate developDate = companyCustomer.getDevelopDate(); if (developDate != null) { if (developDate.isAfter(minDate)) { minDate = developDate; } } if (contract.getSetupDate() != null) { LocalDate setupDate = contract.getSetupDate(); if (setupDate.isBefore(developDate)) { holder.error("合同提交日期 " + setupDate + " 早于客户开发日期 " + developDate + "."); return false; } if (setupDate.isBefore(minDate)) { holder.debug("合同提交日期 " + setupDate + " 早于 " + minDate + ", 跳过"); } else { verifyDates.add(setupDate); } } if (contract.getStartDate() != null) { LocalDate startDate = contract.getStartDate(); if (startDate.isBefore(developDate)) { holder.warn("合同起始日期 " + startDate + " 早于客户开发日期 " + developDate + "."); } if (startDate.isBefore(minDate)) { holder.debug("合同起始日期 " + startDate + " 早于 " + minDate + ", 跳过"); } else { verifyDates.add(startDate); } } if (contract.getOrderDate() != null) { LocalDate orderDate = contract.getOrderDate(); if (orderDate.isBefore(developDate)) { holder.error("合同签订日期 " + orderDate + " 早于客户开发日期 " + developDate + "."); return false; } if (orderDate.isBefore(minDate)) { holder.debug("合同签订日期 " + orderDate + " 早于 " + minDate + ", 跳过"); } else { verifyDates.add(orderDate); } } if (verifyDates.isEmpty()) { holder.warn("合同没有符合可核验的日期"); return false; } // 客户 List files = getCompanyCustomerFileService().findAllByCustomer(companyCustomer); if (files == null || files.isEmpty()) { holder.warn("未见客户评估表"); return false; } for (LocalDate verifyDate : verifyDates) { CustomerFileVo customerFile = files.stream() .filter(v -> v.getSignDate() != null && v.isValid()) .filter(v -> v.getType() == CustomerFileType.EvaluationForm) .filter(v -> MyDateTimeUtils.dateValidFilter(verifyDate, v.getSignDate(), v.getSignDate().plusYears(1), 7)) .findFirst().orElse(null); if (customerFile != null) { return true; } } CustomerFileVo latestFile = files.stream() .filter(v -> v.getSignDate() != null && v.isValid()) .filter(v -> v.getType() == CustomerFileType.EvaluationForm) .max(Comparator.comparing(CustomerFileVo::getSignDate)) .orElse(null); if (latestFile == null) { holder.error("未匹配的客户评估"); return false; } holder.error("客户评估已过期,最后一个客户评估报告日期:" + latestFile.getSignDate() + ", 检测日期:" + verifyDates.stream().map(LocalDate::toString).collect(Collectors.joining(", "))); return false; } private void verifyProject(ContractVo contract, ProjectVo project, MessageHolder holder) { Integer saleTypeId = project.getSaleTypeId(); ProjectSaleTypeVo saleType = null; if (saleTypeId == null) { String code = contract.getCode(); if (code != null && code.length() > 5) { saleType = getSaleTypeService().findByCode(code.substring(0, 1)); if (saleType != null) { project.setSaleTypeId(saleType.getId()); holder.info("根据合同代码设置项目销售类型:" + saleType.getName()); } } } else { saleType = getSaleTypeService().findById(saleTypeId); } if (saleType == null) { holder.warn("销售类型未设置"); } if (project.getAmount() == null || project.getAmount() <= 0) { holder.error("金额小于等于0"); } // boolean needImport = false; ProjectCostService projectCostService = getProjectCostService(); ProjectCostVo autoCost = projectCostService.findAutoCostByProject(project); if (autoCost == null) { // 创建 V0 项目成本 ProjectCostVo cost = projectCostService.newInstanceByProject(project); cost.setVersion(0); cost.setApplicantId(EmployeeService.DEFAULT_SYSTEM_EMPLOYEE_ID); cost.setApplyTime(LocalDateTime.now()); cost.setDescription("自动导入"); autoCost = projectCostService.save(cost); needImport = true; } else { // 检查 V0 项目成本 if (autoCost.getGrossProfitMargin() <= 0) { NumberFormat instance = NumberFormat.getNumberInstance(getLocale()); instance.setMaximumFractionDigits(2); NumberStringConverter converter = new NumberStringConverter(instance); holder.warn("V0项目成本毛利率异常:" + converter.toString(autoCost.getGrossProfitMargin()) + "<=0%"); needImport = true; } } if (needImport && !autoCost.isImportLock()) { holder.debug("更新V0项目成本"); ProjectCostVo cost = projectCostService.findAutoCostByProject(project); if (cost == null) { cost = projectCostService.newInstanceByProject(project); cost.setVersion(0); cost = projectCostService.save(cost); } ProjectCostImportItemsFromContractsTasker tasker = new ProjectCostImportItemsFromContractsTasker(); tasker.setCost(cost); } // 检查最新的项目成本,默认 V1 ProjectCostVo latestCost = projectCostService.findLatestByProject(project); if (latestCost == null) { ProjectCostVo cost = projectCostService.newInstanceByProject(project); cost.setVersion(1); Integer applicantId = project.getApplicantId(); if (applicantId == null) { applicantId = contract.getEmployeeId(); } cost.setApplicantId(applicantId); cost.setApplyTime(project.getCreated().atTime(LocalTime.now())); latestCost = projectCostService.save(cost); } else { // if (latestCost.getGrossProfitMargin() <= 0) { NumberFormat instance = NumberFormat.getNumberInstance(getLocale()); instance.setMaximumFractionDigits(2); NumberStringConverter converter = new NumberStringConverter(instance); holder.warn("V" + latestCost.getVersion() + "项目成本毛利率异常:" + converter.toString(latestCost.getGrossProfitMargin()) + "<=0%"); } else if (latestCost.getGrossProfitMargin() >= 50) { NumberFormat instance = NumberFormat.getNumberInstance(getLocale()); instance.setMaximumFractionDigits(2); NumberStringConverter converter = new NumberStringConverter(instance); holder.warn("V" + latestCost.getVersion() + "项目成本毛利率异常:" + converter.toString(latestCost.getGrossProfitMargin()) + ">=50%"); } } if (project.isUseBid()) { List bids = getProjectBidService().findAllByProject(project); if (bids.isEmpty()) { holder.warn("投标未创建"); } } if (project.isUseOffer()) { List quotations = getProjectQuotationService().findAllByProject(project); if (quotations.isEmpty()) { holder.warn("报价未创建"); } } } private boolean verifyContractFileAsCustomer(ProjectVo project, ContractVo contract, MessageHolder holder) { boolean loseFile = false; List files = getContractFileService().findAllByContract(contract); if (project != null) { // 投标 if (project.isUseBid()) { // 投标审批表 if (!hasFileType(files, ContractFileType.BidApprovalForm)) { holder.error(getFileTypeLocalValue(ContractFileType.BidApprovalForm) + "未上传"); loseFile = true; } // 中标通知书 if (!hasFileType(files, ContractFileType.BidAcceptanceLetter)) { holder.error(getFileTypeLocalValue(ContractFileType.BidAcceptanceLetter) + "未上传"); loseFile = true; } } // 报价 if (project.isUseOffer()) { if (!hasFileType(files, ContractFileType.QuotationApprovalForm)) { holder.error(getFileTypeLocalValue(ContractFileType.QuotationApprovalForm) + "未上传"); loseFile = true; } } Integer saleTypeId = project.getSaleTypeId(); ProjectSaleTypeVo saleType = getSaleTypeService().findById(saleTypeId); if (saleType != null) { List list = getSaleTypeRequireFileTypeService() .findBySaleTypeId(saleType.getId()); for (ProjectSaleTypeRequireFileTypeVo item : list) { ContractFileType fileType = item.getFileType(); if (fileType != null) { if (!hasFileType(files, fileType)) { holder.error(getFileTypeLocalValue(fileType) + "未上传"); loseFile = true; } } } saleType = getSaleTypeService().findById(saleType.getId()); if (saleType.isCriticalProjectDecision()) { if (contract.getAmount() != null && contract.getAmount() >= saleType.getCriticalProjectLimit()) { holder.debug("合同金额 " + contract.getAmount() + " 超过 " + saleType.getCriticalProjectLimit()); if (!hasFileType(files, ContractFileType.CriticalProjectDecisionRecord)) { holder.error(getFileTypeLocalValue(ContractFileType.CriticalProjectDecisionRecord) + "未上传"); loseFile = true; } } } } } if (loseFile && files.isEmpty()) { holder.warn("!上传文件空!"); } return !loseFile; } }