Merge pull request 'develop' (#315) from develop into master

Reviewed-on: http://git.xdadan.loc/tanex/emis-service/pulls/315
This commit is contained in:
heyu 2026-02-06 17:39:57 +08:00
commit c218777763
5 changed files with 227 additions and 118 deletions

View File

@ -714,63 +714,104 @@ public class EmisSettleBillController extends BaseController
{
try {
EmisSettleBill bill=emisBaseService.getSettleBillBySettleBillNo(emisSettleBill.getSettleBillNo());
// 参数校验
if (emisSettleBill == null || StringUtils.isEmpty(emisSettleBill.getSettleBillNo())) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
EmisSettleBill bill = emisBaseService.getSettleBillBySettleBillNo(emisSettleBill.getSettleBillNo());
// 账单不存在校验
if (bill == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 获取子账单数据
List<EmisSettleSubBill> list = emisBaseService.selectSubBillListBySettleBillNo(emisSettleBill.getSettleBillNo());
// 子账单列表为空校验
if (CollectionUtils.isEmpty(list)) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 转换子账单为 MAP
Map<String, Object> textMap = MapUtils.newHashMap();
textMap.put("title", bill.getSettleBillName());
textMap.put("total", bill.getRecMoney().stripTrailingZeros().toPlainString());
textMap.put("title", bill.getSettleBillName() != null ? bill.getSettleBillName() : "");
// 处理可能为null的金额
BigDecimal recMoney = bill.getRecMoney();
textMap.put("total", recMoney != null ? recMoney.stripTrailingZeros().toPlainString() : "0");
String tplName="tpl/bill_tpl_simple.xlsx";
String tplName = "tpl/bill_tpl_simple.xlsx";
// 是否使用复复杂模版,如果有清关费用和其他费用都需要使用复杂模版
boolean isNeedComplexTpl=false;
boolean isNeedComplexTpl = false;
List<Map<String, Object>> listMap = new ArrayList<>();
for (EmisSettleSubBill billItem : list) {
// 跳过waybillDetail为null的项
if (billItem == null || billItem.getWaybillDetail() == null) {
continue;
}
List<Map<String, Object>> listMap=new ArrayList<>();
for(EmisSettleSubBill billItem:list){
Map<String, Object> voMap = new HashMap<>();
voMap.put("sendDate", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,billItem.getWaybillDetail().getSendDate())) ;
voMap.put("billCode",billItem.getBillCode());
voMap.put("fjr",billItem.getWaybillDetail().getSendName()+"--"+billItem.getWaybillDetail().getReceiveName());
voMap.put("kjlx",billItem.getWaybillDetail().getSendSiteName()+"--"+billItem.getWaybillDetail().getDispatchUnderlingSiteName());
// 安全处理日期
Date sendDate = billItem.getWaybillDetail().getSendDate();
voMap.put("sendDate", sendDate != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sendDate) : "");
voMap.put("billCode", billItem.getBillCode() != null ? billItem.getBillCode() : "");
String company=billItem.getWaybillDetail().getCustName();
if(
"1".equals(billItem.getWaybillDetail().getPaymentType()) ||
"3".equals(billItem.getWaybillDetail().getPaymentType()) ||
"5".equals(billItem.getWaybillDetail().getPaymentType())
// 安全处理字符串拼接
String sendName = billItem.getWaybillDetail().getSendName() != null ? billItem.getWaybillDetail().getSendName() : "";
String receiveName = billItem.getWaybillDetail().getReceiveName() != null ? billItem.getWaybillDetail().getReceiveName() : "";
voMap.put("fjr", sendName + "--" + receiveName);
){
company=billItem.getWaybillDetail().getSendCompany();
String sendSiteName = billItem.getWaybillDetail().getSendSiteName() != null ? billItem.getWaybillDetail().getSendSiteName() : "";
String dispatchUnderlingSiteName = billItem.getWaybillDetail().getDispatchUnderlingSiteName() != null ? billItem.getWaybillDetail().getDispatchUnderlingSiteName() : "";
voMap.put("kjlx", sendSiteName + "--" + dispatchUnderlingSiteName);
String company = billItem.getWaybillDetail().getCustName() != null ? billItem.getWaybillDetail().getCustName() : "";
String paymentType = billItem.getWaybillDetail().getPaymentType();
if ("1".equals(paymentType) || "3".equals(paymentType) || "5".equals(paymentType)) {
String sendCompany = billItem.getWaybillDetail().getSendCompany();
company = sendCompany != null ? sendCompany : "";
}
voMap.put("company",company);
voMap.put("paymentType", DictUtils.getDictLabel("emis_payment_type",billItem.getWaybillDetail().getPaymentType()));
voMap.put("productTypeName", billItem.getWaybillDetail().getProductTypeName());
voMap.put("parcelQty", billItem.getWaybillDetail().getParcelQty().toString());
voMap.put("billWeight", billItem.getWaybillDetail().getBillWeight().stripTrailingZeros().toPlainString());
voMap.put("settlementWeight", billItem.getWaybillDetail().getSettlementWeight().stripTrailingZeros().toPlainString());
voMap.put("totalVolume", billItem.getWaybillDetail().getTotalVolume().stripTrailingZeros().toPlainString());
voMap.put("freight", billItem.getWaybillDetail().getFreight().stripTrailingZeros().toPlainString());
voMap.put("company", company);
voMap.put("paymentType", DictUtils.getDictLabel("emis_payment_type", paymentType));
String productTypeName = billItem.getWaybillDetail().getProductTypeName();
voMap.put("productTypeName", productTypeName != null ? productTypeName : "");
String feeRemark="";
if(!StringUtils.isEmpty(billItem.getWaybillDetail().getFeeRemark())){
feeRemark=billItem.getWaybillDetail().getFeeRemark();
// 安全处理Integer转String
Integer parcelQty = billItem.getWaybillDetail().getParcelQty();
voMap.put("parcelQty", parcelQty != null ? parcelQty.toString() : "0");
// 安全处理BigDecimal
BigDecimal billWeight = billItem.getWaybillDetail().getBillWeight();
voMap.put("billWeight", billWeight != null ? billWeight.stripTrailingZeros().toPlainString() : "0");
BigDecimal settlementWeight = billItem.getWaybillDetail().getSettlementWeight();
voMap.put("settlementWeight", settlementWeight != null ? settlementWeight.stripTrailingZeros().toPlainString() : "0");
BigDecimal totalVolume = billItem.getWaybillDetail().getTotalVolume();
voMap.put("totalVolume", totalVolume != null ? totalVolume.stripTrailingZeros().toPlainString() : "0");
BigDecimal freight = billItem.getWaybillDetail().getFreight();
voMap.put("freight", freight != null ? freight.stripTrailingZeros().toPlainString() : "0");
String feeRemark = "";
String feeRemarkValue = billItem.getWaybillDetail().getFeeRemark();
if (!StringUtils.isEmpty(feeRemarkValue)) {
feeRemark = feeRemarkValue;
}
String waybillRemark="";
if(!StringUtils.isEmpty(billItem.getWaybillDetail().getRemark())){
waybillRemark=billItem.getWaybillDetail().getRemark();
String waybillRemark = "";
String remarkValue = billItem.getWaybillDetail().getRemark();
if (!StringUtils.isEmpty(remarkValue)) {
waybillRemark = remarkValue;
}
// if(!StringUtils.isEmpty(billItem.getWaybillDetail().getFeeRemark())){
// feeRemark=feeRemark+" "+billItem.getWaybillDetail().getFeeRemark();
// }
BigDecimal otherFee = BigDecimal.ZERO;
BigDecimal transFee = BigDecimal.ZERO;
BigDecimal cebFee = BigDecimal.ZERO;
@ -780,150 +821,167 @@ public class EmisSettleBillController extends BaseController
String declareFeeStr = "";
String otherFeeStr = "";
String feeItemRemark="";
String feeItemRemark = "";
//计算分项数据
List<EmisSettleSubBillFeeitem> feeItemList=billItem.getFeeitemList();
if( !CollectionUtils.isEmpty(feeItemList)){
// 计算分项数据
List<EmisSettleSubBillFeeitem> feeItemList = billItem.getFeeitemList();
if (!CollectionUtils.isEmpty(feeItemList)) {
for (EmisSettleSubBillFeeitem feeitem : feeItemList) {
// 所有币种全部统一换算成RMB
BigDecimal rmbBillFee=feeitem.getBillFee();
// 币种
String currencyStr="";
if(StringUtils.isEmpty(feeitem.getCurrency())){
feeitem.setCurrency("CNY");
if (feeitem == null) {
continue;
}
if(!"CNY".equals(feeitem.getCurrency())){
currencyStr=""+feeitem.getCurrency()+"";
// 非人民币需要换算成人民币
EmisExchangeRate emisExchangeRate=emisExchangeRateService.getValidExchangeRate(currencyStr,"CNY",DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,billItem.getWaybillDetail().getSendDate()));
double rate=0.0D;
if(emisExchangeRate!=null){
rate=Double.valueOf(emisExchangeRate.getRate());
rmbBillFee=rmbBillFee.multiply(BigDecimal.valueOf(rate));
}
// 所有币种全部统一换算成RMB
BigDecimal billFee = feeitem.getBillFee();
if (billFee == null) {
billFee = BigDecimal.ZERO;
}
BigDecimal rmbBillFee = billFee;
// 币种
String currencyStr = "";
String currency = feeitem.getCurrency();
if (StringUtils.isEmpty(currency)) {
feeitem.setCurrency("CNY");
currency = "CNY";
}
if (!"CNY".equals(currency)) {
currencyStr = currency;
// 非人民币需要换算成人民币
if (sendDate != null) {
String dateStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sendDate);
EmisExchangeRate emisExchangeRate = emisExchangeRateService.getValidExchangeRate(currencyStr, "CNY", dateStr);
if (emisExchangeRate != null && emisExchangeRate.getRate() != null) {
try {
double rate = Double.parseDouble(emisExchangeRate.getRate());
rmbBillFee = rmbBillFee.multiply(BigDecimal.valueOf(rate));
} catch (NumberFormatException e) {
// 汇率转换失败,使用原值
logger.warn("汇率转换失败,币种: {}, 汇率: {}", currencyStr, emisExchangeRate.getRate());
}
}
}
}
// 费用细分
if ("1001".equals(feeitem.getFeeCode())) {
String feeCode = feeitem.getFeeCode();
if ("1001".equals(feeCode)) {
transFee = transFee.add(rmbBillFee);
// transFeeStr= feeitem.getBillFee().stripTrailingZeros().toPlainString()+currencyStr;
}
// 报关费
else if ("2002".equals(feeitem.getFeeCode())) {
else if ("2002".equals(feeCode)) {
cebFee = cebFee.add(rmbBillFee);
// cebFeeStr= feeitem.getBillFee().stripTrailingZeros().toPlainString()+currencyStr;
}
// 清关费
else if ("2003".equals(feeitem.getFeeCode())) {
else if ("2003".equals(feeCode)) {
declareFee = declareFee.add(rmbBillFee);
// declareFeeStr= feeitem.getBillFee().stripTrailingZeros().toPlainString()+currencyStr;
//
} else {
otherFee = otherFee.add(rmbBillFee);
// otherFeeStr= otherFeeStr+""+feeitem.getFeeName()+":"+feeitem.getBillFee().stripTrailingZeros().toPlainString()+currencyStr+",";
//
}
if(
cebFee.compareTo(BigDecimal.ZERO)>0
|| declareFee.compareTo(BigDecimal.ZERO)>0
|| otherFee.compareTo(BigDecimal.ZERO)>0
){
isNeedComplexTpl=true;
if (cebFee.compareTo(BigDecimal.ZERO) > 0
|| declareFee.compareTo(BigDecimal.ZERO) > 0
|| otherFee.compareTo(BigDecimal.ZERO) > 0) {
isNeedComplexTpl = true;
}
if(!StringUtils.isEmpty(feeitem.getRemark())){
if(!feeitem.getRemark().contains("w")){
feeItemRemark=feeItemRemark+"|"+feeitem.getFeeName()+":"+feeitem.getBillFee().stripTrailingZeros().toPlainString()+currencyStr+"(费用说明:"+feeitem.getRemark()+")";
String feeItemRemarkValue = feeitem.getRemark();
if (!StringUtils.isEmpty(feeItemRemarkValue)) {
if (!feeItemRemarkValue.contains("w")) {
String feeName = feeitem.getFeeName() != null ? feeitem.getFeeName() : "";
String billFeeStr = billFee.stripTrailingZeros().toPlainString();
feeItemRemark = feeItemRemark + "|" + feeName + ":" + billFeeStr + currencyStr + "(费用说明:" + feeItemRemarkValue + ")";
}
}
}
transFeeStr= transFee.stripTrailingZeros().toPlainString();
cebFeeStr= cebFee.stripTrailingZeros().toPlainString();
declareFeeStr= declareFee.stripTrailingZeros().toPlainString();
otherFeeStr= otherFee.stripTrailingZeros().toPlainString();
}else{
transFeeStr=billItem.getWaybillDetail().getFreight().stripTrailingZeros().toPlainString();
transFeeStr = transFee.stripTrailingZeros().toPlainString();
cebFeeStr = cebFee.stripTrailingZeros().toPlainString();
declareFeeStr = declareFee.stripTrailingZeros().toPlainString();
otherFeeStr = otherFee.stripTrailingZeros().toPlainString();
} else {
// 如果没有费用项列表,使用运单的运费
if (freight != null) {
transFeeStr = freight.stripTrailingZeros().toPlainString();
} else {
transFeeStr = "0";
}
}
if(StringUtils.isEmpty(transFeeStr)){
transFeeStr="0";
if (StringUtils.isEmpty(transFeeStr)) {
transFeeStr = "0";
}
if(StringUtils.isEmpty(cebFeeStr)){
cebFeeStr="0";
if (StringUtils.isEmpty(cebFeeStr)) {
cebFeeStr = "0";
}
if(StringUtils.isEmpty(declareFeeStr)){
declareFeeStr="0";
if (StringUtils.isEmpty(declareFeeStr)) {
declareFeeStr = "0";
}
if(StringUtils.isEmpty(otherFeeStr)){
otherFeeStr="0";
if (StringUtils.isEmpty(otherFeeStr)) {
otherFeeStr = "0";
}
voMap.put("transFee",transFeeStr);
voMap.put("cebFee",cebFeeStr);
voMap.put("declareFee",declareFeeStr);
voMap.put("transFee", transFeeStr);
voMap.put("cebFee", cebFeeStr);
voMap.put("declareFee", declareFeeStr);
otherFeeStr=StringUtils.removeEnd(otherFeeStr,",");
otherFeeStr = StringUtils.removeEnd(otherFeeStr, ",");
voMap.put("otherFee",otherFeeStr);
voMap.put("otherFee", otherFeeStr);
if(!StringUtils.isEmpty(feeItemRemark)){
feeItemRemark="费用项:"+StringUtils.removeEnd(feeItemRemark,"|");
if (!StringUtils.isEmpty(feeItemRemark)) {
feeItemRemark = "费用项:" + StringUtils.removeEnd(feeItemRemark, "|");
}
// feeRemark=feeRemark+","+feeItemRemark;
feeRemark=StringUtils.removeEnd(feeRemark,",")+"";
feeRemark = StringUtils.removeEnd(feeRemark, ",") + "";
voMap.put("feeRemark", feeRemark+" "+waybillRemark);
voMap.put("feeRemark", feeRemark + " " + waybillRemark);
listMap.add(voMap);
}
if(isNeedComplexTpl) {
if (isNeedComplexTpl) {
tplName = "tpl/bill_tpl.xlsx";
}else{
} else {
tplName = "tpl/bill_tpl_simple.xlsx";
}
ServletOutputStream outputStream = response.getOutputStream();
//账单主数据填充
InputStream inputStream = ResourceUtil.getResourceObj(tplName).getStream();
// 账单主数据填充
InputStream inputStream = ResourceUtil.getResourceObj(tplName).getStream();
// 设置创建行的方式
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
// FillConfig fillConfig2 = FillConfig.builder().forceNewRow(Boolean.FALSE).build();
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).build();
// 写入到sheet
WriteSheet sheet0 = EasyExcel.writerSheet(0).build();
excelWriter.fill(textMap, sheet0);
// 填充
logger.error("====>listMap===>{}",JSON.toJSONString(listMap));
logger.error("====>listMap===>{}", JSON.toJSONString(listMap));
excelWriter.fill(listMap, fillConfig, sheet0);
// 设置响应头信息
String fileName = bill.getSettleBillName()+".xlsx";
String billName = bill.getSettleBillName();
String fileName = (billName != null ? billName : "账单") + ".xlsx";
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
response.setContentType("application/octet-stream");
//关流
// 关流
excelWriter.finish();
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} catch (IOException e) {
logger.error("导出账单失败", e);
e.printStackTrace();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (Exception e) {
logger.error("导出账单异常", e);
e.printStackTrace();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}

View File

@ -10,9 +10,11 @@
*/
package com.xdadan.erp.emis.mapper;
import java.math.BigDecimal;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xdadan.erp.emis.domain.EmisWriteoffApplyDetail;
import org.apache.ibatis.annotations.Param;
/**
* @ClassName EmisWriteoffApplyDetailMapper
@ -104,4 +106,14 @@ public interface EmisWriteoffApplyDetailMapper extends BaseMapper<EmisWriteoffAp
* @return 结果
*/
public int deleteEmisWriteoffApplyDetailByIds(Long[] ids);
/**
* 根据运单号汇总历史申请金额(仅统计财务中心未通过的数据:bl_center_confirmed != 1)
*
* @param billCode 运单号
* @param excludeApplyId 排除的申请ID(用于修改时避免把当前申请算入历史)
* @return 历史申请金额合计(无数据返回0)
*/
BigDecimal sumHistoryApplyMoneyByBillCode(@Param("billCode") String billCode,
@Param("excludeApplyId") Long excludeApplyId);
}

View File

@ -172,6 +172,10 @@ public class EmisTmsSiteBatchDtlServiceImpl implements IEmisTmsSiteBatchDtlServi
return null;
}
if(!"1".equals(emisWaybill.getOrderStatus())){
throw new EmisBizError(EmisBizErrorType.FAIL, "未接单");
}
// 创建返回结果对象
EmisTmsSiteBatchDtl result = new EmisTmsSiteBatchDtl();
result.setWaybillDetail(emisWaybill);

View File

@ -121,6 +121,21 @@ public class EmisWriteoffApplyServiceImpl extends EmisBaseService implements IEm
* 验证:申请金额+已收金额 <= 运单应收金额
*/
private void validateWriteoffAmount(EmisWriteoffApply emisWriteoffApply) throws EmisBizError {
validateWriteoffAmount(emisWriteoffApply, false);
}
/**
* 验证销账金额
*
* <p>
* - 来自 auditCenterPass:申请金额 + 已收金额 <= 账单应收金额
* - 非 auditCenterPass:申请金额 + 该运单历史申请金额(所有 bl_center_confirmed != 1 的申请明细之和) + 已收金额 <= 账单应收金额
* </p>
*
* @param fromAuditCenterPass 是否来自财务中心审核通过(auditCenterPass)
*/
private void validateWriteoffAmount(EmisWriteoffApply emisWriteoffApply, boolean fromAuditCenterPass)
throws EmisBizError {
if (CollectionUtils.isEmpty(emisWriteoffApply.getDetailList())) {
throw new EmisBizError("销账申请明细不能为空");
}
@ -170,9 +185,16 @@ public class EmisWriteoffApplyServiceImpl extends EmisBaseService implements IEm
}
// 验证:申请金额 + 已收金额 <= 账单应收金额
BigDecimal totalAmount = detail.getApplyMoney().add(receivedAmount);
BigDecimal historyApplyAmount = BigDecimal.ZERO;
if (!fromAuditCenterPass) {
BigDecimal dbSum = emisWriteoffApplyDetailMapper.sumHistoryApplyMoneyByBillCode(detail.getBillCode(),
emisWriteoffApply.getId());
historyApplyAmount = dbSum != null ? dbSum : BigDecimal.ZERO;
}
BigDecimal totalAmount = detail.getApplyMoney().add(historyApplyAmount).add(receivedAmount);
if (totalAmount.compareTo(recMoney) > 0) {
throw new EmisBizError("运单号 " + detail.getBillCode() + " 的申请金额(" + detail.getApplyMoney()
throw new EmisBizError("运单号 " + detail.getBillCode() + " 的累计申请金额(" + detail.getApplyMoney().add(historyApplyAmount)
+ ") + 已收金额(" + receivedAmount + ") = " + totalAmount + " 超过运单应收金额("
+ recMoney + ")");
}
@ -431,8 +453,8 @@ public class EmisWriteoffApplyServiceImpl extends EmisBaseService implements IEm
.selectEmisWriteoffApplyDetailListByApplyIdWithDetails(existingApply.getId());
existingApply.setDetailList(detailList);
// 验证销账金额
validateWriteoffAmount(existingApply);
// 验证销账金额(财务中心审核通过场景不纳入“历史申请金额”)
validateWriteoffAmount(existingApply, true);
// 先更新相关业务数据(在更新审核状态之前进行验证和更新)
detailList = updateRelatedBusinessData(existingApply, true);

View File

@ -252,6 +252,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join emis_site dest_site on w.dispatch_underling_site_code = dest_site.site_code and dest_site.del_flag = '0'
</sql>
<!-- 根据运单号汇总历史申请金额(仅统计财务中心未通过的数据:bl_center_confirmed != 1) -->
<select id="sumHistoryApplyMoneyByBillCode" resultType="java.math.BigDecimal">
select ifnull(sum(wad.apply_money), 0)
from emis_writeoff_apply_detail wad
inner join emis_writeoff_apply wa on wa.id = wad.apply_id and wa.del_flag = '0'
where wad.del_flag = '0'
and wad.bill_code = #{billCode}
and wa.bl_center_confirmed = '0'
<if test="excludeApplyId != null">
and wa.id != #{excludeApplyId}
</if>
</select>
<select id="selectEmisWriteoffApplyDetailList" parameterType="EmisWriteoffApplyDetail" resultMap="EmisWriteoffApplyDetailResult">
<include refid="selectEmisWriteoffApplyDetailVo"/>
<where>