将ContextUtils重命名为BeanContext,并统一客户端和服务端的实现 添加DefaultBeanContext作为默认实现 优化Inventory同步任务逻辑,支持WebSocket远程调用 更新tasker_mapper.json添加新的任务映射 移除未使用的syncInventory方法
784 lines
32 KiB
Java
784 lines
32 KiB
Java
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> T getBean(Class<T> requiredType) throws BeansException {
|
||
return parent.getBean(requiredType);
|
||
}
|
||
|
||
@Override
|
||
public <T> T getCachedBean(Class<T> 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<ContractFileType, ContractFileTypeLocal> 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<ContractVo> 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<ContractFileVo> files = fileService.findAllByContract(contract);
|
||
List<VendorGroupRequireFileTypeVo> 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<ContractBidVendorVo> 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<ContractFileVo> 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<LocalDate> 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<CustomerFileVo> 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<ProjectBidVo> bids = getProjectBidService().findAllByProject(project);
|
||
if (bids.isEmpty()) {
|
||
holder.warn("投标未创建");
|
||
}
|
||
}
|
||
if (project.isUseOffer()) {
|
||
List<ProjectQuotationVo> quotations = getProjectQuotationService().findAllByProject(project);
|
||
if (quotations.isEmpty()) {
|
||
holder.warn("报价未创建");
|
||
}
|
||
}
|
||
}
|
||
|
||
private boolean verifyContractFileAsCustomer(ProjectVo project, ContractVo contract, MessageHolder holder) {
|
||
boolean loseFile = false;
|
||
List<ContractFileVo> 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<ProjectSaleTypeRequireFileTypeVo> 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;
|
||
}
|
||
}
|