|
|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
package org.dromara.oa.base.service.impl;
|
|
|
|
|
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import org.dromara.common.core.enums.FormatsType;
|
|
|
|
|
import org.dromara.common.core.exception.ServiceException;
|
|
|
|
|
import org.dromara.common.core.utils.DateUtils;
|
|
|
|
|
@ -10,23 +11,24 @@ import org.dromara.oa.base.domain.vo.BaseTemplateVariableVo;
|
|
|
|
|
import org.dromara.oa.base.domain.vo.TemplateVariableAssignVo;
|
|
|
|
|
import org.dromara.oa.base.service.IBaseTemplateVariableService;
|
|
|
|
|
import org.dromara.oa.base.service.ITemplateVariableAssignService;
|
|
|
|
|
import org.dromara.oa.crm.domain.bo.CrmQuoteMaterialBo;
|
|
|
|
|
import org.dromara.oa.crm.domain.vo.CrmQuoteInfoVo;
|
|
|
|
|
import org.dromara.oa.crm.domain.vo.CrmQuoteMaterialVo;
|
|
|
|
|
import org.dromara.oa.crm.service.ICrmQuoteInfoService;
|
|
|
|
|
import org.dromara.oa.crm.service.ICrmQuoteMaterialService;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpContractInfoVo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpContractMaterialVo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpContractPaymentMethodVo;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpContractInfoService;
|
|
|
|
|
import org.dromara.oa.erp.service.IErpContractMaterialService;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.math.RoundingMode;
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -43,6 +45,10 @@ public class TemplateVariableAssignServiceImpl implements ITemplateVariableAssig
|
|
|
|
|
private final IErpContractInfoService erpContractInfoService;
|
|
|
|
|
private final IErpContractMaterialService erpContractMaterialService;
|
|
|
|
|
|
|
|
|
|
// 报价单
|
|
|
|
|
private final ICrmQuoteInfoService crmQuoteInfoService;
|
|
|
|
|
private final ICrmQuoteMaterialService crmQuoteMaterialService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据模板类型为模板变量赋值
|
|
|
|
|
* 模板类型:1=合同,2=发货单,3=项目变更,4=报价单
|
|
|
|
|
@ -64,8 +70,11 @@ public class TemplateVariableAssignServiceImpl implements ITemplateVariableAssig
|
|
|
|
|
// 项目变更类型
|
|
|
|
|
return assignDeliveryTemplateVariables(request);
|
|
|
|
|
} else if ("4".equals(templateType)) {
|
|
|
|
|
if (request.getQuoteId() == null) {
|
|
|
|
|
throw new ServiceException("报价单ID不能为空");
|
|
|
|
|
}
|
|
|
|
|
// 报价单类型
|
|
|
|
|
return assignDeliveryTemplateVariables(request);
|
|
|
|
|
return assignQuoteTemplateVariables(request);
|
|
|
|
|
} else {
|
|
|
|
|
throw new ServiceException("不支持的模板类型:" + templateType);
|
|
|
|
|
}
|
|
|
|
|
@ -443,5 +452,227 @@ public class TemplateVariableAssignServiceImpl implements ITemplateVariableAssig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 为报价单模板变量赋值
|
|
|
|
|
*/
|
|
|
|
|
private List<TemplateVariableAssignVo> assignQuoteTemplateVariables(TemplateVariableAssignRequest request) {
|
|
|
|
|
// 1) 按模板类型查询所有变量配置,确保字段/数据源信息齐备
|
|
|
|
|
BaseTemplateVariableBo queryBo = new BaseTemplateVariableBo();
|
|
|
|
|
queryBo.setTemplateType(request.getTemplateType());
|
|
|
|
|
List<BaseTemplateVariableVo> allTemplateVariables = baseTemplateVariableService.queryList(queryBo);
|
|
|
|
|
if (allTemplateVariables == null) {
|
|
|
|
|
allTemplateVariables = new ArrayList<>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2) 查询报价单主表信息,缺失则直接报错
|
|
|
|
|
CrmQuoteInfoVo quoteInfo = crmQuoteInfoService.queryById(request.getQuoteId());
|
|
|
|
|
if (StringUtils.isNull(quoteInfo)) {
|
|
|
|
|
throw new ServiceException("报价单信息不存在,报价单ID:" + request.getQuoteId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) 如存在物料数据源需求,则查询物料并补充序号
|
|
|
|
|
List<CrmQuoteMaterialVo> quoteMaterials = null;
|
|
|
|
|
boolean needMaterialData = allTemplateVariables.stream()
|
|
|
|
|
.anyMatch(var -> "crm_quote_material".equalsIgnoreCase(var.getDataSource()));
|
|
|
|
|
if (needMaterialData) {
|
|
|
|
|
CrmQuoteMaterialBo materialBo = new CrmQuoteMaterialBo();
|
|
|
|
|
materialBo.setQuoteId(request.getQuoteId());
|
|
|
|
|
quoteMaterials = crmQuoteMaterialService.queryList(materialBo);
|
|
|
|
|
if (quoteMaterials == null) {
|
|
|
|
|
quoteMaterials = new ArrayList<>();
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < quoteMaterials.size(); i++) {
|
|
|
|
|
CrmQuoteMaterialVo material = quoteMaterials.get(i);
|
|
|
|
|
material.setSeq(material.getItemNo() != null ? material.getItemNo() : (i + 1L));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buildAssignResultListForQuote(allTemplateVariables, quoteInfo, quoteMaterials);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建报价单赋值结果列表
|
|
|
|
|
*/
|
|
|
|
|
private List<TemplateVariableAssignVo> buildAssignResultListForQuote(
|
|
|
|
|
List<BaseTemplateVariableVo> allTemplateVariables,
|
|
|
|
|
CrmQuoteInfoVo quoteInfo,
|
|
|
|
|
List<CrmQuoteMaterialVo> quoteMaterials) {
|
|
|
|
|
|
|
|
|
|
List<TemplateVariableAssignVo> resultList = new ArrayList<>();
|
|
|
|
|
for (BaseTemplateVariableVo variable : allTemplateVariables) {
|
|
|
|
|
if (variable == null || StringUtils.isBlank(variable.getVarName())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1) 先处理报价单特殊变量(金额大小写、质保期等)
|
|
|
|
|
Object value = resolveSpecialQuoteValue(variable, quoteInfo);
|
|
|
|
|
|
|
|
|
|
// 2) 根据变量数据源从主表或明细表取值
|
|
|
|
|
if (value == null) {
|
|
|
|
|
value = getValueFromQuoteDataSource(variable, quoteInfo, quoteMaterials);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) 若取不到值则回退默认值
|
|
|
|
|
if (value == null && StringUtils.isNotBlank(variable.getDefaultValue())) {
|
|
|
|
|
value = variable.getDefaultValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4) 按变量类型(数组/时间/文本)转换输出格式
|
|
|
|
|
Object finalValue = convertValueToTargetType(value, variable);
|
|
|
|
|
|
|
|
|
|
TemplateVariableAssignVo assignVo = new TemplateVariableAssignVo();
|
|
|
|
|
assignVo.setVarName(variable.getVarName());
|
|
|
|
|
assignVo.setVarLabel(variable.getVarLabel());
|
|
|
|
|
assignVo.setVarValue(finalValue);
|
|
|
|
|
assignVo.setVarType(variable.getVarType());
|
|
|
|
|
resultList.add(assignVo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return resultList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 报价单特殊变量处理(优先于 data_field 反射)
|
|
|
|
|
* 兼容已上线模板占位符,不要求每次都改变量配置即可输出大写/小写金额与质保期
|
|
|
|
|
*/
|
|
|
|
|
private Object resolveSpecialQuoteValue(BaseTemplateVariableVo variable, CrmQuoteInfoVo quoteInfo) {
|
|
|
|
|
if (variable == null || quoteInfo == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (!"crm_quote_info".equalsIgnoreCase(variable.getDataSource())) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String normalizedVarName = normalizeVariableName(variable.getVarName());
|
|
|
|
|
String dataField = variable.getDataField();
|
|
|
|
|
BigDecimal totalAmount = quoteInfo.getTotalIncludingTax() != null
|
|
|
|
|
? quoteInfo.getTotalIncludingTax()
|
|
|
|
|
: quoteInfo.getTotalPrice();
|
|
|
|
|
|
|
|
|
|
if ("总价大写".equals(normalizedVarName)
|
|
|
|
|
|| "totalAmountUppercase".equalsIgnoreCase(dataField)
|
|
|
|
|
|| "capitalizedAmount".equalsIgnoreCase(dataField)) {
|
|
|
|
|
return toChineseUpperMoney(totalAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ("总价小写".equals(normalizedVarName)
|
|
|
|
|
|| "totalAmountLowercase".equalsIgnoreCase(dataField)) {
|
|
|
|
|
return formatMoney(totalAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ("质保期".equals(normalizedVarName)
|
|
|
|
|
|| "warrantyPeriodDescription".equalsIgnoreCase(dataField)) {
|
|
|
|
|
return StringUtils.isNotBlank(quoteInfo.getRemark()) ? quoteInfo.getRemark() : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String formatMoney(BigDecimal amount) {
|
|
|
|
|
if (amount == null) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
return amount.setScale(2, RoundingMode.HALF_UP).toPlainString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String toChineseUpperMoney(BigDecimal amount) {
|
|
|
|
|
if (amount == null) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
BigDecimal normalized = amount.setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
if (normalized.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
|
return "零元整";
|
|
|
|
|
}
|
|
|
|
|
// 超出本实现支持上限时返回数值字符串,避免导出中断
|
|
|
|
|
if (normalized.abs().compareTo(new BigDecimal("9999999999999999.99")) > 0) {
|
|
|
|
|
return normalized.toPlainString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final String[] digits = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
|
|
|
|
final String[] units = {"分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆"};
|
|
|
|
|
|
|
|
|
|
long value = normalized.movePointRight(2).abs().longValue();
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
int unitIndex = 0;
|
|
|
|
|
boolean zeroPending = false;
|
|
|
|
|
|
|
|
|
|
while (value > 0) {
|
|
|
|
|
int digit = (int) (value % 10);
|
|
|
|
|
if (digit == 0) {
|
|
|
|
|
if ((unitIndex == 2 || unitIndex == 6 || unitIndex == 10 || unitIndex == 14)
|
|
|
|
|
&& sb.length() > 0
|
|
|
|
|
&& sb.indexOf(units[unitIndex]) < 0) {
|
|
|
|
|
sb.insert(0, units[unitIndex]);
|
|
|
|
|
}
|
|
|
|
|
if (!zeroPending && unitIndex > 1 && sb.length() > 0) {
|
|
|
|
|
sb.insert(0, "零");
|
|
|
|
|
zeroPending = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sb.insert(0, units[unitIndex]);
|
|
|
|
|
sb.insert(0, digits[digit]);
|
|
|
|
|
zeroPending = false;
|
|
|
|
|
}
|
|
|
|
|
value /= 10;
|
|
|
|
|
unitIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String result = sb.toString()
|
|
|
|
|
.replaceAll("零{2,}", "零")
|
|
|
|
|
.replaceAll("零(万|亿|兆)", "$1")
|
|
|
|
|
.replace("亿万", "亿")
|
|
|
|
|
.replaceAll("零角零分$", "整")
|
|
|
|
|
.replaceAll("零分$", "整")
|
|
|
|
|
.replaceAll("零角", "零");
|
|
|
|
|
|
|
|
|
|
if (!result.contains("元")) {
|
|
|
|
|
result = "零元" + result;
|
|
|
|
|
}
|
|
|
|
|
if (!result.endsWith("整") && !result.endsWith("分")) {
|
|
|
|
|
result = result + "整";
|
|
|
|
|
}
|
|
|
|
|
if (normalized.signum() < 0) {
|
|
|
|
|
result = "负" + result;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 报价单数据源取值
|
|
|
|
|
*/
|
|
|
|
|
private Object getValueFromQuoteDataSource(BaseTemplateVariableVo variable,
|
|
|
|
|
CrmQuoteInfoVo quoteInfo,
|
|
|
|
|
List<CrmQuoteMaterialVo> quoteMaterials) {
|
|
|
|
|
if (StringUtils.isBlank(variable.getDataField())) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String dataSource = variable.getDataSource();
|
|
|
|
|
// 明细数组数据源:取子表字段列表
|
|
|
|
|
if ("crm_quote_material".equalsIgnoreCase(dataSource)) {
|
|
|
|
|
return getFieldValueFromQuoteMaterialList(quoteMaterials, variable.getDataField());
|
|
|
|
|
// 主表数据源:直接取报价单字段
|
|
|
|
|
} else if ("crm_quote_info".equalsIgnoreCase(dataSource)) {
|
|
|
|
|
return getFieldValueFromObject(quoteInfo, variable.getDataField());
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<Object> getFieldValueFromQuoteMaterialList(List<CrmQuoteMaterialVo> quoteMaterials, String fieldName) {
|
|
|
|
|
if (quoteMaterials == null || quoteMaterials.isEmpty() || StringUtils.isBlank(fieldName)) {
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Object> values = new ArrayList<>();
|
|
|
|
|
for (CrmQuoteMaterialVo material : quoteMaterials) {
|
|
|
|
|
// 逐条提取字段值,空值用空字符串占位,保证长度对齐
|
|
|
|
|
Object value = getFieldValueFromObject(material, fieldName);
|
|
|
|
|
values.add(value != null ? value : "");
|
|
|
|
|
}
|
|
|
|
|
return values;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|