This commit is contained in:
2026-09-07 16:08:05 +08:00
parent 397e9f3b62
commit 2e7ab6f508
14 changed files with 320 additions and 67 deletions
@@ -128,12 +128,20 @@ public class ImportFailureExcelUtil {
}
private static List<Field> excelFields(Class<?> excelClass) {
return Arrays.stream(excelClass.getDeclaredFields())
List<Class<?>> classHierarchy = new ArrayList<>();
for (Class<?> current = excelClass; current != null; current = current.getSuperclass()) {
classHierarchy.add(0, current);
}
List<Field> fields = new ArrayList<>();
for (Class<?> current : classHierarchy) {
Arrays.stream(current.getDeclaredFields())
.filter(field -> field.getAnnotation(ExcelProperty.class) != null)
.filter(field -> field.getAnnotation(ExcelIgnore.class) == null)
.filter(field -> !Objects.equals(field.getName(), ERROR_MESSAGE_FIELD))
.filter(field -> !Objects.equals(field.getName(), FAILURE_REASON_FIELD))
.toList();
.forEach(fields::add);
}
return fields;
}
private static List<List<String>> buildHead(List<Field> excelFields) {
@@ -36,6 +36,7 @@ public class FormalSettlementVO extends FormalSettlement {
@TableField(exist = false) @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate createStartDate;
@TableField(exist = false) @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate createEndDate;
@TableField(exist = false) private String createUserName;
@TableField(exist = false) private String ids;
@TableField(exist = false) private String approvalStatusName;
@TableField(exist = false) private String settlementTypeName;
@TableField(exist = false) private String preSettlementNos;
@@ -69,6 +69,9 @@ public class PreSettlementVO extends PreSettlement {
@Schema(description = "创建人姓名")
private String createUserName;
@TableField(exist = false)
private String ids;
@TableField(exist = false)
@Schema(description = "更新人姓名")
private String updateUserName;
@@ -18,6 +18,7 @@ import java.util.List;
public class TransportReconciliationVO extends TransportReconciliation {
@Serial private static final long serialVersionUID = 1L;
@TableField(exist = false) private String createUserName;
@TableField(exist = false) private String ids;
@TableField(exist = false) private String updateUserName;
@TableField(exist = false) private String reconciliationModeName;
@TableField(exist = false) private String reconciliationStatusName;
@@ -10,14 +10,18 @@ package org.springblade.transport.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest;
import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest;
@@ -26,6 +30,7 @@ import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest;
import org.springblade.transport.pojo.dto.PreSettlementDetailAdjustRequest;
import org.springblade.transport.pojo.entity.FormalSettlementDetailFee;
import org.springblade.transport.pojo.vo.FormalSettlementVO;
import org.springblade.transport.excel.FormalSettlementExcel;
import org.springblade.transport.pojo.vo.PreSettlementVO;
import org.springblade.transport.service.IFormalSettlementService;
import org.springblade.transport.service.IPreSettlementService;
@@ -39,6 +44,8 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 正式结算单控制器
@@ -62,6 +69,65 @@ public class FormalSettlementController extends BladeController {
return R.data(formalSettlementService.selectPage(Condition.getPage(pageQuery), query));
}
@GetMapping("/export")
@ApiOperationSupport(order = 20)
@Operation(summary = "导出正式结算单")
public void export(FormalSettlementVO query, @RequestParam(required = false) String ids,
HttpServletResponse response) {
query.setIds(ids);
IPage<FormalSettlementVO> page = formalSettlementService.selectPage(new Page<>(1, 100000), query);
List<FormalSettlementExcel> rows = page.getRecords().stream().map(this::toExcel).toList();
ExcelUtil.export(response, "正式结算单" + DateUtil.time(), "正式结算单", rows, FormalSettlementExcel.class);
}
private FormalSettlementExcel toExcel(FormalSettlementVO vo) {
FormalSettlementExcel excel = new FormalSettlementExcel();
excel.setFormalSettlementNo(vo.getFormalSettlementNo());
excel.setPreSettlementNos(vo.getPreSettlementNos());
excel.setSourceType(vo.getSourceType());
excel.setPayerName(vo.getPayerName());
excel.setPayeeName(vo.getPayeeName());
excel.setProjectName(vo.getProjectName());
excel.setDeptName(vo.getDeptName());
excel.setContractNo(vo.getContractNo());
excel.setContractName(vo.getContractName());
excel.setSettlementAmount(formatMoney(vo.getSettlementAmount(), vo.getCurrency()));
excel.setLocalSettlementAmount(formatMoney(vo.getLocalSettlementAmount(), vo.getLocalCurrency()));
excel.setExchangeRate(vo.getExchangeRate() == null ? "" : vo.getExchangeRate().stripTrailingZeros().toPlainString());
excel.setInvoiceStatusName(invoiceStatusName(vo.getInvoiceStatus(), vo.getSettlementType()));
excel.setPaymentStatusName(paymentStatusName(vo.getPaymentStatus()));
excel.setApprovalStatusName(vo.getApprovalStatusName());
excel.setKingdeeBillNo(vo.getKingdeeBillNo());
excel.setCreateUserName(vo.getCreateUserName());
excel.setCreateTime(vo.getCreateTime());
return excel;
}
private String formatMoney(BigDecimal value, String currency) {
if (value == null) return "";
return value.setScale(2, RoundingMode.HALF_UP).toPlainString() + " "
+ (currency == null || currency.isBlank() ? "RMB" : currency);
}
private String invoiceStatusName(String value, String settlementType) {
boolean payable = "payable".equals(settlementType);
return switch (value == null ? "" : value) {
case "unreceived" -> payable ? "未收票" : "未开票";
case "partial" -> payable ? "部分收票" : "部分开票";
case "completed" -> payable ? "已收票" : "已开票";
default -> value == null || value.isBlank() ? "-" : value;
};
}
private String paymentStatusName(String value) {
return switch (value == null ? "" : value) {
case "unpaid" -> "未收/付款";
case "partial" -> "部分收/付款";
case "paid" -> "已收/付款";
default -> value == null || value.isBlank() ? "-" : value;
};
}
@GetMapping("/detail")
@ApiOperationSupport(order = 2)
@Operation(summary = "正式结算单详情")
@@ -224,7 +224,9 @@ public class PreSettlementController extends BladeController {
@GetMapping("/export")
@ApiOperationSupport(order = 19)
@Operation(summary = "导出预结算单")
public void export(PreSettlementVO query, HttpServletResponse response) {
public void export(PreSettlementVO query, @RequestParam(required = false) String ids,
HttpServletResponse response) {
query.setIds(ids);
IPage<PreSettlementVO> page = preSettlementService.selectPage(new Page<>(1, 100000), query);
List<PreSettlementExcel> rows = page.getRecords().stream().map(this::toExcel).toList();
ExcelUtil.export(response, "预结算单" + DateUtil.time(), "预结算单", rows, PreSettlementExcel.class);
@@ -7,6 +7,7 @@
package org.springblade.transport.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -23,6 +24,7 @@ import org.springblade.transport.excel.CargoReconciliationExcel;
import org.springblade.transport.excel.CargoReconciliationFailureExcel;
import org.springblade.transport.excel.VehicleReconciliationExcel;
import org.springblade.transport.excel.VehicleReconciliationFailureExcel;
import org.springblade.transport.excel.TransportReconciliationExportExcel;
import org.springblade.transport.pojo.dto.TransportReconciliationManualMatchRequest;
import org.springblade.transport.pojo.dto.TransportReconciliationSaveRequest;
import org.springblade.transport.pojo.entity.FormalSettlement;
@@ -40,6 +42,8 @@ import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.math.BigDecimal;
import java.math.RoundingMode;
/** 运输对账单控制器。 @author Chill */
@RestController
@@ -57,6 +61,40 @@ public class TransportReconciliationController extends BladeController {
return R.data(reconciliationService.selectPage(Condition.getPage(pageQuery), query));
}
@GetMapping("/export")
@ApiOperationSupport(order = 15)
@Operation(summary = "导出运输对账单")
public void export(TransportReconciliationVO query, @RequestParam(required = false) String ids,
HttpServletResponse response) {
query.setIds(ids);
IPage<TransportReconciliationVO> page = reconciliationService.selectPage(new Page<>(1, 100000), query);
List<TransportReconciliationExportExcel> rows = page.getRecords().stream().map(this::toExcel).toList();
ExcelUtil.export(response, "运输对账" + DateUtil.time(), "运输对账", rows, TransportReconciliationExportExcel.class);
}
private TransportReconciliationExportExcel toExcel(TransportReconciliationVO vo) {
TransportReconciliationExportExcel excel = new TransportReconciliationExportExcel();
excel.setReconciliationNo(vo.getReconciliationNo());
excel.setPayerName(vo.getPayerName());
excel.setPayeeName(vo.getPayeeName());
excel.setProjectName(vo.getProjectName());
excel.setDeptName(vo.getDeptName());
excel.setContractNo(vo.getContractNo());
excel.setContractName(vo.getContractName());
excel.setSettlementAmount(formatMoney(vo.getSettlementAmount()));
excel.setReconciliationModeName(vo.getReconciliationModeName());
excel.setExternalBillCount(vo.getExternalBillCount());
excel.setMatchedCount(vo.getMatchedCount());
excel.setReconciliationStatusName(vo.getReconciliationStatusName());
excel.setCreateUserName(vo.getCreateUserName());
excel.setCreateTime(vo.getCreateTime());
return excel;
}
private String formatMoney(BigDecimal value) {
return value == null ? "0.00" : value.setScale(2, RoundingMode.HALF_UP).toPlainString();
}
@GetMapping("/detail")
@ApiOperationSupport(order = 2)
@Operation(summary = "运输对账详情")
@@ -0,0 +1,58 @@
package org.springblade.transport.excel;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import cn.idev.excel.annotation.write.style.ContentRowHeight;
import cn.idev.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
@Data
@ColumnWidth(22)
@HeadRowHeight(20)
@ContentRowHeight(18)
public class FormalSettlementExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty("结算单号")
private String formalSettlementNo;
@ExcelProperty("预结算单号")
private String preSettlementNos;
@ExcelProperty("来源")
private String sourceType;
@ExcelProperty("付款方")
private String payerName;
@ExcelProperty("收款方")
private String payeeName;
@ExcelProperty("项目名称")
private String projectName;
@ExcelProperty("所属组织")
private String deptName;
@ExcelProperty("合同编号")
private String contractNo;
@ExcelProperty("合同名称")
private String contractName;
@ExcelProperty("原币结算金额")
private String settlementAmount;
@ExcelProperty("本位币结算金额")
private String localSettlementAmount;
@ExcelProperty("结算汇率")
private String exchangeRate;
@ExcelProperty("发票状态")
private String invoiceStatusName;
@ExcelProperty("收付款状态")
private String paymentStatusName;
@ExcelProperty("审核状态")
private String approvalStatusName;
@ExcelProperty("金蝶单据号")
private String kingdeeBillNo;
@ExcelProperty("创建人")
private String createUserName;
@ExcelProperty("创建时间")
private Date createTime;
}
@@ -0,0 +1,50 @@
package org.springblade.transport.excel;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import cn.idev.excel.annotation.write.style.ContentRowHeight;
import cn.idev.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
@Data
@ColumnWidth(22)
@HeadRowHeight(20)
@ContentRowHeight(18)
public class TransportReconciliationExportExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty("对账单号")
private String reconciliationNo;
@ExcelProperty("付款方")
private String payerName;
@ExcelProperty("收款方")
private String payeeName;
@ExcelProperty("项目名称")
private String projectName;
@ExcelProperty("所属组织")
private String deptName;
@ExcelProperty("合同编号")
private String contractNo;
@ExcelProperty("合同名称")
private String contractName;
@ExcelProperty("结算金额")
private String settlementAmount;
@ExcelProperty("对账模式")
private String reconciliationModeName;
@ExcelProperty("账单总数")
private Integer externalBillCount;
@ExcelProperty("匹配数")
private Integer matchedCount;
@ExcelProperty("对账状态")
private String reconciliationStatusName;
@ExcelProperty("创建人")
private String createUserName;
@ExcelProperty("创建时间")
private Date createTime;
}
@@ -153,6 +153,9 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
.eq(Func.isNotEmpty(query.getApprovalStatus()), FormalSettlement::getApprovalStatus, query.getApprovalStatus())
.ge(query.getCreateStartDate() != null, FormalSettlement::getCreateTime, query.getCreateStartDate() == null ? null : query.getCreateStartDate().atStartOfDay())
.lt(query.getCreateEndDate() != null, FormalSettlement::getCreateTime, query.getCreateEndDate() == null ? null : query.getCreateEndDate().plusDays(1).atStartOfDay());
if (Func.isNotEmpty(query.getIds())) {
wrapper.in(FormalSettlement::getId, Func.toLongList(query.getIds()));
}
if (Func.isNotEmpty(query.getPreSettlementNo())) {
List<Long> ids = sourceMapper.selectList(Wrappers.<FormalSettlementSource>lambdaQuery()
.like(FormalSettlementSource::getPreSettlementNo, query.getPreSettlementNo()))
@@ -668,7 +668,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
}
private LambdaQueryWrapper<PreSettlement> buildQuery(PreSettlementVO query) {
return Wrappers.<PreSettlement>lambdaQuery()
LambdaQueryWrapper<PreSettlement> wrapper = Wrappers.<PreSettlement>lambdaQuery()
.eq(PreSettlement::getIsDeleted, 0)
.like(Func.isNotEmpty(query.getPreSettlementNo()), PreSettlement::getPreSettlementNo,
query.getPreSettlementNo())
@@ -688,6 +688,10 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
.le(query.getCreateEndDate() != null, PreSettlement::getCreateTime,
query.getCreateEndDate() == null ? null : query.getCreateEndDate().plusDays(1).atStartOfDay())
.orderByDesc(PreSettlement::getCreateTime);
if (Func.isNotEmpty(query.getIds())) {
wrapper.in(PreSettlement::getId, Func.toLongList(query.getIds()));
}
return wrapper;
}
private Map<String, Object> candidateMap(ReceivablePayableDetail source) {
@@ -725,6 +729,9 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
result.put("unitPrice", source.getUnitPrice());
result.put("freightAmount", money(source.getFreightAmount()));
result.put("feeItemsJson", source.getFeeItemsJson());
BigDecimal originalAmount = sourceOriginalAmount(source);
result.put("originalAmount", originalAmount);
result.put("adjustAmount", money(source.getTotalAmount()).subtract(originalAmount));
result.put("totalAmount", money(source.getTotalAmount()));
result.put("currency", source.getCurrency());
result.put("settlementStatusName", "待结算");
@@ -768,6 +775,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
PreSettlementDetail detail = copySourceDetail(settlement, source);
detailMapper.insert(detail);
copySourceFees(detail, source);
refreshDetail(detail);
int affected = sourceDetailMapper.update(null,
Wrappers.<ReceivablePayableDetail>lambdaUpdate()
.set(ReceivablePayableDetail::getPreSettlementNo, settlement.getPreSettlementNo())
@@ -834,8 +842,9 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
detail.setUnitPrice(source.getUnitPrice());
detail.setFreightAmount(money(source.getFreightAmount()));
detail.setFeeItemsJson(source.getFeeItemsJson());
detail.setOriginalAmount(money(source.getTotalAmount()));
detail.setAdjustAmount(BigDecimal.ZERO.setScale(2));
BigDecimal originalAmount = sourceOriginalAmount(source);
detail.setOriginalAmount(originalAmount);
detail.setAdjustAmount(money(source.getTotalAmount()).subtract(originalAmount));
detail.setSettlementAmountTax(money(source.getTotalAmount()));
detail.setCurrency(Func.isEmpty(source.getCurrency()) ? LOCAL_CURRENCY : source.getCurrency());
detail.setRemark(source.getRemark());
@@ -849,6 +858,18 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
return detail;
}
private BigDecimal sourceOriginalAmount(ReceivablePayableDetail source) {
List<ReceivablePayableCargoFee> sourceFees = sourceFeeMapper.selectList(
Wrappers.<ReceivablePayableCargoFee>lambdaQuery()
.eq(ReceivablePayableCargoFee::getDetailId, source.getId())
.eq(ReceivablePayableCargoFee::getIsDeleted, 0));
if (sourceFees.isEmpty() || sourceFees.stream().noneMatch(item -> item.getOriginalAmount() != null)) {
return money(source.getTotalAmount());
}
return sourceFees.stream().map(ReceivablePayableCargoFee::getOriginalAmount)
.map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
}
private void copySourceFees(PreSettlementDetail detail, ReceivablePayableDetail source) {
List<ReceivablePayableCargoFee> sourceFees = sourceFeeMapper.selectList(
Wrappers.<ReceivablePayableCargoFee>lambdaQuery()
@@ -880,7 +901,18 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
fee.setId(null);
fee.setPreSettlementDetailId(detail.getId());
fee.setSourceFeeId(sourceFee.getId());
fee.setSettlementAmountTax(money(sourceFee.getAfterAmount()));
// 历史应付费用可能没有 after_amount,不能在首次打开明细调整时被当成 0。
BigDecimal settlementAmount = sourceFee.getAfterAmount() != null
? money(sourceFee.getAfterAmount())
: sourceFees.size() == 1 && source.getTotalAmount() != null
? money(source.getTotalAmount())
: sourceFee.getOriginalAmount() != null
? money(sourceFee.getOriginalAmount()) : BigDecimal.ZERO.setScale(2);
BigDecimal originalAmount = sourceFee.getOriginalAmount() == null
? settlementAmount : money(sourceFee.getOriginalAmount());
fee.setOriginalAmount(originalAmount);
fee.setAdjustAmount(settlementAmount.subtract(originalAmount));
fee.setSettlementAmountTax(settlementAmount);
fee.setSettlementAmountNoTax(null);
detailFeeMapper.insert(fee);
}
@@ -938,10 +970,11 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
row.setLineNo(lineNo++);
row.setFeeType(keyParts[0]);
row.setFeeItem(keyParts.length > 1 ? keyParts[1] : "");
row.setOriginalAmount(money(entry.getValue()));
row.setAdjustAmount(preserveAdjustments && old != null ? money(old.getAdjustAmount())
: BigDecimal.ZERO.setScale(2));
row.setSettlementAmount(row.getOriginalAmount().add(row.getAdjustAmount()));
// 原金额是明细首次生成时的快照,多次调整后不得被当前结算金额覆盖。
// 当前费用聚合值代表调整后的结算金额,差额统一记录为调整金额。
row.setOriginalAmount(old == null ? money(entry.getValue()) : money(old.getOriginalAmount()));
row.setSettlementAmount(money(entry.getValue()));
row.setAdjustAmount(money(row.getSettlementAmount().subtract(row.getOriginalAmount())));
row.setRemark(old == null ? "" : old.getRemark());
row.setManualFlag(0);
summaryFeeMapper.insert(row);
@@ -1023,11 +1056,12 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
}
row.setFeeType(feeType);
row.setFeeItem(feeItem);
row.setOriginalAmount(requestRow.getOriginalAmount() == null
? money(row.getOriginalAmount()) : money(requestRow.getOriginalAmount()));
// 生成费用行的三个金额必须来源于结算明细快照,不能接受前端回传值覆盖原金额。
// rebuildSummaryFees 已按明细重建 original/adjust/settlement,此处仅保留备注。
BigDecimal before = money(row.getAdjustAmount());
row.setAdjustAmount(money(requestRow.getAdjustAmount()));
row.setSettlementAmount(money(row.getOriginalAmount()).add(row.getAdjustAmount()));
row.setOriginalAmount(money(row.getOriginalAmount()));
row.setSettlementAmount(money(row.getSettlementAmount()));
row.setAdjustAmount(money(row.getSettlementAmount().subtract(row.getOriginalAmount())));
row.setRemark(limitRemark(requestRow.getRemark(), 50));
if (row.getId() == null) summaryFeeMapper.insert(row); else summaryFeeMapper.updateById(row);
retainedGeneratedIds.add(row.getId());
@@ -1071,8 +1105,8 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
}
private void refreshSettlementAmount(PreSettlement settlement) {
BigDecimal settlementAmount = listSummaryFees(settlement.getId()).stream()
.map(PreSettlementSummaryFee::getSettlementAmount).map(this::money)
BigDecimal settlementAmount = listDetails(settlement.getId()).stream()
.map(PreSettlementDetail::getSettlementAmountTax).map(this::money)
.reduce(BigDecimal.ZERO, BigDecimal::add);
settlement.setSettlementAmount(money(settlementAmount));
BigDecimal rate = normalizeRate(settlement.getExchangeRate(), settlement.getCurrency());
@@ -1523,13 +1523,8 @@ public class ReceivablePayableDetailServiceImpl
}
private Map<String, Object> resolveBillingPlan(List<Map<String, Object>> plans, String planId) {
if ("__matched__".equals(planId)) {
return plans.stream().filter(this::isDefaultPlan).findFirst()
.orElseGet(() -> plans.isEmpty() ? null : plans.get(plans.size() - 1));
}
if (Func.isEmpty(planId)) {
return plans.stream().filter(this::isDefaultPlan).findFirst()
.orElseGet(() -> plans.isEmpty() ? null : plans.get(plans.size() - 1));
if ("__matched__".equals(planId) || Func.isEmpty(planId)) {
return resolveDefaultBillingPlan(plans, null);
}
return plans.stream().filter(plan -> Objects.equals(stringValue(plan, "id"), planId)
|| Objects.equals(stringValue(plan, "planId"), planId)
@@ -1545,20 +1540,24 @@ public class ReceivablePayableDetailServiceImpl
return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value)) || "1".equals(String.valueOf(value));
}
/**
* 解析合同生效计费方案。
* 合同存在勾选默认的计费方案时仅在默认方案内匹配计算,不再回退其他方案;
* 全部方案均未勾选默认时仅使用最新添加的计费方案。
*/
private Map<String, Object> resolveDefaultBillingPlan(List<Map<String, Object>> plans, String transportType) {
if (plans.isEmpty()) return null;
if (plans.size() == 1) return plans.get(0);
Optional<Map<String, Object>> matchedDefaultPlan = plans.stream().filter(this::isDefaultPlan)
List<Map<String, Object>> defaultPlans = plans.stream().filter(this::isDefaultPlan).toList();
if (defaultPlans.isEmpty()) return plans.get(plans.size() - 1);
Optional<Map<String, Object>> matchedDefaultPlan = defaultPlans.stream()
.filter(plan -> !isBlank(plan.get("transportMode")))
.filter(plan -> matchesCondition(plan.get("transportMode"), transportType))
.findFirst();
if (matchedDefaultPlan.isPresent()) return matchedDefaultPlan.get();
Optional<Map<String, Object>> defaultPlanWithoutTransportMode = plans.stream()
.filter(this::isDefaultPlan)
return defaultPlans.stream()
.filter(plan -> isBlank(plan.get("transportMode")))
.findFirst();
if (defaultPlanWithoutTransportMode.isPresent()) return defaultPlanWithoutTransportMode.get();
return plans.stream().noneMatch(this::isDefaultPlan) ? plans.get(plans.size() - 1) : null;
.findFirst()
.orElseGet(() -> defaultPlans.get(defaultPlans.size() - 1));
}
private BigDecimal calculateRule(Map<String, Object> rule, Waybill waybill) {
@@ -1937,17 +1936,17 @@ public class ReceivablePayableDetailServiceImpl
return waybill;
}
/**
* 匹配费用调整试算使用的合同计费规则,仅在合同生效计费方案内查找。
*/
private List<Map<String, Object>> matchingAdjustedRules(ContractManage contract,
ReceivablePayableCargoFee fee, Waybill waybill) {
Map<String, Object> plan = resolveDefaultBillingPlan(parseList(contract.getBillingPlanJson()),
waybill.getTransportType());
if (plan == null || !(plan.get("rules") instanceof List<?> rules)) return List.of();
Set<String> feeItemNames = parseMap(fee.getFeeItemsJson()).keySet();
List<Map<String, Object>> firstCandidates = List.of();
List<Map<String, Object>> defaultCandidates = List.of();
List<Map<String, Object>> billingMatchedCandidates = List.of();
List<Map<String, Object>> billingFieldCandidates = List.of();
for (Map<String, Object> plan : parseList(contract.getBillingPlanJson())) {
if (!(plan.get("rules") instanceof List<?> rules)) continue;
List<Map<String, Object>> candidates = new ArrayList<>();
List<Map<String, Object>> feeItemCandidates = new ArrayList<>();
List<Map<String, Object>> candidates = new ArrayList<>();
for (Object value : rules) {
if (!(value instanceof Map<?, ?> raw)) continue;
Map<String, Object> rule = new LinkedHashMap<>();
@@ -1956,23 +1955,9 @@ public class ReceivablePayableDetailServiceImpl
feeItemCandidates.add(rule);
if (matchesRule(rule, waybill)) candidates.add(rule);
}
if (feeItemCandidates.stream().anyMatch(rule -> matchesBillingFields(rule, fee))) {
if (billingFieldCandidates.isEmpty() || isDefaultPlan(plan)) {
billingFieldCandidates = feeItemCandidates;
}
}
if (candidates.isEmpty()) continue;
if (firstCandidates.isEmpty()) firstCandidates = candidates;
if (isDefaultPlan(plan)) defaultCandidates = candidates;
if (candidates.stream().anyMatch(rule -> matchesBillingFields(rule, fee))) {
if (billingMatchedCandidates.isEmpty() || isDefaultPlan(plan)) {
billingMatchedCandidates = candidates;
}
}
}
if (!billingMatchedCandidates.isEmpty()) return billingMatchedCandidates;
if (!billingFieldCandidates.isEmpty()) return billingFieldCandidates;
return defaultCandidates.isEmpty() ? firstCandidates : defaultCandidates;
if (candidates.stream().anyMatch(rule -> matchesBillingFields(rule, fee))) return candidates;
if (feeItemCandidates.stream().anyMatch(rule -> matchesBillingFields(rule, fee))) return feeItemCandidates;
return candidates;
}
private boolean matchesBillingFields(Map<String, Object> rule, ReceivablePayableCargoFee fee) {
@@ -116,6 +116,9 @@ public class TransportReconciliationServiceImpl
.eq(Func.isNotEmpty(query.getMatchStatus()), TransportReconciliation::getMatchStatus, query.getMatchStatus())
.eq(Func.isNotEmpty(query.getReconciliationStatus()), TransportReconciliation::getReconciliationStatus, query.getReconciliationStatus())
.orderByDesc(TransportReconciliation::getCreateTime);
if (Func.isNotEmpty(query.getIds())) {
wrapper.in(TransportReconciliation::getId, Func.toLongList(query.getIds()));
}
return page(page, wrapper).convert(item -> TransportReconciliationWrapper.build().entityVO(item));
}
+1
View File
@@ -417,6 +417,7 @@ INSERT IGNORE INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `pa
(2090000000000000612, 2090000000000000600, 'waybill_manage_template', '下载模板', 'waybill_manage_template', '', '', 12, 2, 0, 1, NULL, '', 0),
(2090000000000000613, 2090000000000000600, 'waybill_manage_road_loading', '公路配载', 'waybill_manage_road_loading', '', '', 13, 2, 0, 1, NULL, '', 0),
(2090000000000000614, 2090000000000000600, 'waybill_manage_mileage', '维护里程', 'waybill_manage_mileage', '', '', 14, 2, 0, 1, NULL, '', 0),
(2090000000000000615, 2090000000000000000, 'waybill_import', '导入运单', 'waybill_import', '/business/waybill-import', 'iconfont icon-daoru', 65, 1, 0, 1, NULL, '', 0),
(2090000000000000700, 2090000000000000000, 'loading_manage', '配载管理', 'loading_manage', '/business/loading-manage', 'iconfont icon-caidanguanli', 70, 1, 0, 1, NULL, '', 0),
(2090000000000000701, 2090000000000000700, 'loading_manage_view', '查看', 'loading_manage_view', '', '', 1, 2, 0, 1, NULL, '', 0),
(2090000000000000702, 2090000000000000700, 'loading_manage_add', '新增', 'loading_manage_add', '', '', 2, 2, 0, 1, NULL, '', 0),