|
|
|
|
@ -2,6 +2,7 @@ package org.dromara.oa.erp.service.impl;
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.apache.seata.spring.annotation.GlobalTransactional;
|
|
|
|
|
@ -12,8 +13,13 @@ import org.dromara.common.core.utils.MapstructUtils;
|
|
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
|
|
import org.dromara.common.satoken.utils.LoginHelper;
|
|
|
|
|
import org.dromara.common.tenant.helper.TenantHelper;
|
|
|
|
|
import org.dromara.oa.api.constant.OrderInvoiceStatusConstant;
|
|
|
|
|
import org.dromara.oa.api.constant.OrderPurchaseStatusConstant;
|
|
|
|
|
import org.dromara.oa.api.enums.ContractOrderStatusTypeEnum;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpContractInfo;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpFinInvoiceInfo;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpProjectInfo;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpProjectPurchase;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpProjectContracts;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpProjectPlan;
|
|
|
|
|
import org.dromara.oa.erp.domain.ErpProjectPlanStage;
|
|
|
|
|
@ -25,8 +31,10 @@ import org.dromara.oa.erp.domain.vo.ErpContractOrderPurchaseMaterialVo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpContractInfoMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpContractMaterialMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpFinInvoiceInfoMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpProjectInfoMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpProjectContractsMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpProjectPurchaseMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpProjectPlanMapper;
|
|
|
|
|
import org.dromara.oa.erp.mapper.ErpProjectPlanStageMapper;
|
|
|
|
|
import org.dromara.oa.crm.domain.vo.CrmCustomerInfoVo;
|
|
|
|
|
@ -35,6 +43,7 @@ import org.dromara.oa.erp.service.IErpContractChangeService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpContractOrderService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpProjectContractsService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpProjectInfoService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpProjectPlanService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpProjectTypeService;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpProjectTypeVo;
|
|
|
|
|
import org.dromara.oa.erp.constant.ProjectCategoryConstant;
|
|
|
|
|
@ -44,6 +53,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
import com.github.yulichang.toolkit.JoinWrappers;
|
|
|
|
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
|
|
|
|
import org.dromara.system.api.RemoteCodeRuleService;
|
|
|
|
|
import org.dromara.wms.api.RemoteWmsShippingBillService;
|
|
|
|
|
import org.dromara.workflow.api.RemoteWorkflowService;
|
|
|
|
|
import org.dromara.workflow.api.domain.RemoteStartProcess;
|
|
|
|
|
import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt;
|
|
|
|
|
@ -52,6 +62,7 @@ import org.springframework.context.event.EventListener;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.apache.dubbo.config.annotation.DubboReference;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
@ -79,9 +90,12 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService {
|
|
|
|
|
private final ErpContractInfoMapper contractInfoMapper;
|
|
|
|
|
private final ErpContractMaterialMapper contractMaterialMapper;
|
|
|
|
|
private final ErpProjectContractsMapper projectContractsMapper;
|
|
|
|
|
private final ErpProjectPurchaseMapper projectPurchaseMapper;
|
|
|
|
|
private final ErpFinInvoiceInfoMapper finInvoiceInfoMapper;
|
|
|
|
|
private final IErpProjectContractsService projectContractsService;
|
|
|
|
|
|
|
|
|
|
private final IErpContractChangeService erpContractChangeService;
|
|
|
|
|
private final IErpProjectPlanService erpProjectPlanService;
|
|
|
|
|
private final IErpProjectInfoService projectInfoService;
|
|
|
|
|
private final IErpProjectTypeService projectTypeService;
|
|
|
|
|
private final ICrmCustomerInfoService crmCustomerInfoService;
|
|
|
|
|
@ -97,6 +111,9 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService {
|
|
|
|
|
@DubboReference(timeout = 30000)
|
|
|
|
|
private RemoteCodeRuleService remoteCodeRuleService;
|
|
|
|
|
|
|
|
|
|
@DubboReference(timeout = 30000)
|
|
|
|
|
private RemoteWmsShippingBillService remoteWmsShippingBillService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询合同订单(项目信息)
|
|
|
|
|
*
|
|
|
|
|
@ -659,6 +676,263 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 刷新合同订单台账业务状态(采购/发货/开票/回款)
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void refreshContractOrderStatus(Long contractId, Long projectId, String statusType) {
|
|
|
|
|
ContractOrderStatusTypeEnum type = ContractOrderStatusTypeEnum.getByCode(statusType);
|
|
|
|
|
if (type == null) {
|
|
|
|
|
log.warn("不支持的合同订单状态类型: {}", statusType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (type) {
|
|
|
|
|
case PAYMENT -> erpProjectPlanService.refreshContractOrderPaymentRateByContractId(contractId);
|
|
|
|
|
case DELIVERY -> refreshDeliveryStatus(contractId);
|
|
|
|
|
case PURCHASE -> refreshPurchaseStatus(contractId);
|
|
|
|
|
case INVOICE -> refreshInvoiceStatus(contractId);
|
|
|
|
|
default -> log.warn("未处理的状态类型: {}", statusType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void refreshDeliveryStatus(Long contractId) {
|
|
|
|
|
if (contractId == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
String deliveryStatus = remoteWmsShippingBillService.resolveOrderDeliveryStatusByContractId(contractId);
|
|
|
|
|
updateContractOrdersField(contractId, deliveryStatus, ContractOrderStatusTypeEnum.DELIVERY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void refreshInvoiceStatus(Long contractId) {
|
|
|
|
|
if (contractId == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
String invoiceStatus = resolveInvoiceStatus(contractId);
|
|
|
|
|
updateContractOrdersField(contractId, invoiceStatus, ContractOrderStatusTypeEnum.INVOICE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void refreshPurchaseStatus(Long contractId) {
|
|
|
|
|
if (contractId == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
List<ErpProjectInfo> contractOrders = listContractOrdersByContractId(contractId);
|
|
|
|
|
if (CollUtil.isEmpty(contractOrders)) {
|
|
|
|
|
log.warn("未找到合同订单,contractId={}", contractId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (ErpProjectInfo contractOrder : contractOrders) {
|
|
|
|
|
String purchaseStatus = resolvePurchaseStatus(contractOrder);
|
|
|
|
|
if (Objects.equals(purchaseStatus, contractOrder.getOrderPurchaseStatus())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ErpProjectInfo update = new ErpProjectInfo();
|
|
|
|
|
update.setProjectId(contractOrder.getProjectId());
|
|
|
|
|
update.setOrderPurchaseStatus(purchaseStatus);
|
|
|
|
|
projectInfoMapper.updateById(update);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateContractOrdersField(Long contractId, String statusValue, ContractOrderStatusTypeEnum type) {
|
|
|
|
|
List<ErpProjectInfo> contractOrders = listContractOrdersByContractId(contractId);
|
|
|
|
|
if (CollUtil.isEmpty(contractOrders)) {
|
|
|
|
|
log.warn("未找到合同订单,contractId={}", contractId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (ErpProjectInfo contractOrder : contractOrders) {
|
|
|
|
|
String current = switch (type) {
|
|
|
|
|
case DELIVERY -> contractOrder.getOrderDeliveryStatus();
|
|
|
|
|
case INVOICE -> contractOrder.getOrderInvoiceStatus();
|
|
|
|
|
default -> null;
|
|
|
|
|
};
|
|
|
|
|
if (Objects.equals(statusValue, current)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ErpProjectInfo update = new ErpProjectInfo();
|
|
|
|
|
update.setProjectId(contractOrder.getProjectId());
|
|
|
|
|
switch (type) {
|
|
|
|
|
case DELIVERY -> update.setOrderDeliveryStatus(statusValue);
|
|
|
|
|
case INVOICE -> update.setOrderInvoiceStatus(statusValue);
|
|
|
|
|
default -> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
projectInfoMapper.updateById(update);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<ErpProjectInfo> listContractOrdersByContractId(Long contractId) {
|
|
|
|
|
return projectInfoMapper.selectList(Wrappers.<ErpProjectInfo>lambdaQuery()
|
|
|
|
|
.eq(ErpProjectInfo::getContractId, contractId)
|
|
|
|
|
.eq(ErpProjectInfo::getProjectCategory, ProjectCategoryConstant.CONTRACT_ORDER)
|
|
|
|
|
.eq(ErpProjectInfo::getDelFlag, "0"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按合同下开票记录汇总计算开票状态。
|
|
|
|
|
*/
|
|
|
|
|
private String resolveInvoiceStatus(Long contractId) {
|
|
|
|
|
List<ErpFinInvoiceInfo> invoices = finInvoiceInfoMapper.selectList(Wrappers.<ErpFinInvoiceInfo>lambdaQuery()
|
|
|
|
|
.eq(ErpFinInvoiceInfo::getContractId, contractId)
|
|
|
|
|
.eq(ErpFinInvoiceInfo::getDelFlag, "0"));
|
|
|
|
|
if (CollUtil.isEmpty(invoices)) {
|
|
|
|
|
return OrderInvoiceStatusConstant.NOT_INVOICED;
|
|
|
|
|
}
|
|
|
|
|
boolean hasCompleted = false;
|
|
|
|
|
boolean hasInProgress = false;
|
|
|
|
|
BigDecimal completedAmount = BigDecimal.ZERO;
|
|
|
|
|
for (ErpFinInvoiceInfo invoice : invoices) {
|
|
|
|
|
if (OAStatusEnum.APPROVING.getStatus().equals(invoice.getInvoiceStatus())
|
|
|
|
|
|| BusinessStatusEnum.WAITING.getStatus().equals(invoice.getFlowStatus())) {
|
|
|
|
|
hasInProgress = true;
|
|
|
|
|
}
|
|
|
|
|
if (OAStatusEnum.COMPLETED.getStatus().equals(invoice.getInvoiceStatus())) {
|
|
|
|
|
hasCompleted = true;
|
|
|
|
|
if (invoice.getIssueAmount() != null) {
|
|
|
|
|
completedAmount = completedAmount.add(invoice.getIssueAmount());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hasCompleted) {
|
|
|
|
|
return hasInProgress ? OrderInvoiceStatusConstant.PARTIAL_INVOICED : OrderInvoiceStatusConstant.NOT_INVOICED;
|
|
|
|
|
}
|
|
|
|
|
BigDecimal contractAmount = resolveContractAmount(contractId);
|
|
|
|
|
if (contractAmount.compareTo(BigDecimal.ZERO) <= 0 || completedAmount.compareTo(contractAmount) >= 0) {
|
|
|
|
|
return OrderInvoiceStatusConstant.INVOICED;
|
|
|
|
|
}
|
|
|
|
|
return OrderInvoiceStatusConstant.PARTIAL_INVOICED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按采购匹配清单与在途采购单计算采购状态。
|
|
|
|
|
*/
|
|
|
|
|
private String resolvePurchaseStatus(ErpProjectInfo contractOrder) {
|
|
|
|
|
Long bizProjectId = resolvePurchaseBizProjectId(contractOrder);
|
|
|
|
|
if (bizProjectId == null) {
|
|
|
|
|
return OrderPurchaseStatusConstant.NOT_PURCHASED;
|
|
|
|
|
}
|
|
|
|
|
Long contractId = contractOrder.getContractId();
|
|
|
|
|
if (hasPurchasingInProgress(bizProjectId, contractId)) {
|
|
|
|
|
return OrderPurchaseStatusConstant.PURCHASING;
|
|
|
|
|
}
|
|
|
|
|
List<ErpContractOrderPurchaseMaterialVo> materials = queryPurchaseMaterialList(bizProjectId);
|
|
|
|
|
if (CollUtil.isEmpty(materials)) {
|
|
|
|
|
return OrderPurchaseStatusConstant.NOT_PURCHASED;
|
|
|
|
|
}
|
|
|
|
|
boolean hasPurchased = false;
|
|
|
|
|
boolean allPurchased = true;
|
|
|
|
|
boolean hasRequiredMaterial = false;
|
|
|
|
|
for (ErpContractOrderPurchaseMaterialVo material : materials) {
|
|
|
|
|
if (contractId != null && material.getContractId() != null
|
|
|
|
|
&& !Objects.equals(contractId, material.getContractId())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
BigDecimal contractAmount = material.getContractAmount() == null ? BigDecimal.ZERO : material.getContractAmount();
|
|
|
|
|
if (contractAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
hasRequiredMaterial = true;
|
|
|
|
|
BigDecimal purchasedAmount = material.getPurchasedAmount() == null ? BigDecimal.ZERO : material.getPurchasedAmount();
|
|
|
|
|
if (purchasedAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
hasPurchased = true;
|
|
|
|
|
}
|
|
|
|
|
BigDecimal unpurchased = material.getUnpurchasedAmount() == null ? BigDecimal.ZERO : material.getUnpurchasedAmount();
|
|
|
|
|
if (unpurchased.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
allPurchased = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hasRequiredMaterial || !hasPurchased) {
|
|
|
|
|
return OrderPurchaseStatusConstant.NOT_PURCHASED;
|
|
|
|
|
}
|
|
|
|
|
if (allPurchased) {
|
|
|
|
|
return OrderPurchaseStatusConstant.PURCHASED;
|
|
|
|
|
}
|
|
|
|
|
return OrderPurchaseStatusConstant.PARTIAL_PURCHASED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasPurchasingInProgress(Long bizProjectId, Long contractId) {
|
|
|
|
|
LambdaQueryWrapper<ErpProjectPurchase> lqw = Wrappers.<ErpProjectPurchase>lambdaQuery()
|
|
|
|
|
.eq(ErpProjectPurchase::getProjectId, bizProjectId)
|
|
|
|
|
.eq(ErpProjectPurchase::getDelFlag, "0")
|
|
|
|
|
.and(w -> w.eq(ErpProjectPurchase::getProjectPurchaseStatus, OAStatusEnum.APPROVING.getStatus())
|
|
|
|
|
.or()
|
|
|
|
|
.eq(ErpProjectPurchase::getFlowStatus, BusinessStatusEnum.WAITING.getStatus()));
|
|
|
|
|
if (contractId != null) {
|
|
|
|
|
lqw.eq(ErpProjectPurchase::getRelationId, contractId);
|
|
|
|
|
}
|
|
|
|
|
return projectPurchaseMapper.selectCount(lqw) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 解析采购查询用的实施项目ID(与订单台账前端口径一致)。
|
|
|
|
|
*/
|
|
|
|
|
private Long resolvePurchaseBizProjectId(ErpProjectInfo contractOrder) {
|
|
|
|
|
if (contractOrder == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (!ProjectCategoryConstant.CONTRACT_ORDER.equals(contractOrder.getProjectCategory())) {
|
|
|
|
|
return contractOrder.getProjectId();
|
|
|
|
|
}
|
|
|
|
|
if (contractOrder.getParentProjectId() != null) {
|
|
|
|
|
return contractOrder.getParentProjectId();
|
|
|
|
|
}
|
|
|
|
|
Long contractId = contractOrder.getContractId();
|
|
|
|
|
if (contractId == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
List<ErpProjectContracts> relations = projectContractsMapper.selectList(Wrappers.<ErpProjectContracts>lambdaQuery()
|
|
|
|
|
.eq(ErpProjectContracts::getContractId, contractId)
|
|
|
|
|
.eq(ErpProjectContracts::getDelFlag, "0")
|
|
|
|
|
.orderByAsc(ErpProjectContracts::getSortOrder)
|
|
|
|
|
.orderByAsc(ErpProjectContracts::getProjectContractsId));
|
|
|
|
|
if (CollUtil.isEmpty(relations)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
Set<Long> projectIds = relations.stream()
|
|
|
|
|
.map(ErpProjectContracts::getProjectId)
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
if (CollUtil.isEmpty(projectIds)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
List<ErpProjectInfo> projects = projectInfoMapper.selectList(Wrappers.<ErpProjectInfo>lambdaQuery()
|
|
|
|
|
.in(ErpProjectInfo::getProjectId, projectIds)
|
|
|
|
|
.ne(ErpProjectInfo::getProjectCategory, ProjectCategoryConstant.CONTRACT_ORDER)
|
|
|
|
|
.eq(ErpProjectInfo::getDelFlag, "0")
|
|
|
|
|
.last("limit 1"));
|
|
|
|
|
return CollUtil.isEmpty(projects) ? null : projects.get(0).getProjectId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BigDecimal resolveContractAmount(Long contractId) {
|
|
|
|
|
ErpContractInfo contractInfo = contractInfoMapper.selectById(contractId);
|
|
|
|
|
if (contractInfo != null && contractInfo.getTotalPrice() != null
|
|
|
|
|
&& contractInfo.getTotalPrice().compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
return contractInfo.getTotalPrice();
|
|
|
|
|
}
|
|
|
|
|
List<ErpProjectInfo> orders = listContractOrdersByContractId(contractId);
|
|
|
|
|
return orders.stream()
|
|
|
|
|
.map(ErpProjectInfo::getAmount)
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Long resolveContractIdByProjectId(Long projectId) {
|
|
|
|
|
if (projectId == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
ErpProjectInfo project = projectInfoMapper.selectById(projectId);
|
|
|
|
|
if (project == null || !"0".equals(project.getDelFlag())) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (project.getContractId() != null) {
|
|
|
|
|
return project.getContractId();
|
|
|
|
|
}
|
|
|
|
|
ErpProjectContracts relation = projectContractsMapper.selectOne(Wrappers.<ErpProjectContracts>lambdaQuery()
|
|
|
|
|
.eq(ErpProjectContracts::getProjectId, projectId)
|
|
|
|
|
.eq(ErpProjectContracts::getDelFlag, "0")
|
|
|
|
|
.last("limit 1"), false);
|
|
|
|
|
return relation == null ? null : relation.getContractId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等)
|
|
|
|
|
*
|
|
|
|
|
|