This commit is contained in:
2026-08-31 10:38:14 +08:00
parent a2b604f3a3
commit 12996eb739
23 changed files with 602 additions and 72 deletions
@@ -24,6 +24,7 @@ public class FormalSettlementSaveRequest implements Serializable {
private String settlementType;
private List<Long> sourcePreSettlementIds;
private List<Long> sourceDetailIds;
private List<DetailAdjustment> detailAdjustments;
private LocalDate exchangeRateDate;
private BigDecimal exchangeRate;
private String attachmentsJson;
@@ -31,6 +32,14 @@ public class FormalSettlementSaveRequest implements Serializable {
private List<SummaryFee> summaryFees;
private List<Invoice> invoices;
@Data
public static class DetailAdjustment implements Serializable {
@Serial private static final long serialVersionUID = 1L;
private Long sourcePreSettlementDetailId;
private Long sourceDetailId;
private BigDecimal adjustAmount;
}
@Data
public static class SummaryFee implements Serializable {
@Serial private static final long serialVersionUID = 1L;
@@ -22,6 +22,7 @@ public class SettlementAdjustmentSaveRequest implements Serializable {
private Long formalSettlementDetailFeeId;
private String feeType;
private String feeItem;
private BigDecimal originalAmountTax;
private BigDecimal adjustmentAmountTax;
private BigDecimal adjustmentAmountNoTax;
private String remark;
@@ -0,0 +1,56 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.transport.pojo.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 运单里程维护请求
*
* @author Chill
*/
@Data
@Schema(description = "运单里程维护请求")
public class WaybillMileageRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "运单ID")
private Long id;
@Schema(description = "里程(公里)")
private BigDecimal mileage;
@Schema(description = "里程维护备注")
private String mileageRemark;
}
@@ -54,7 +54,7 @@ public class ReceivablePayableCargoFee extends TenantEntity {
@Schema(description = "行号")
private String lineNo;
@Schema(description = "来源:自动生成/手录入")
@Schema(description = "来源:自动生成/手录入")
private String dataSource;
@Schema(description = "货物名称")
@@ -75,6 +75,9 @@ public class ReceivablePayableCargoFee extends TenantEntity {
@Schema(description = "计费类型")
private String billingType;
@Schema(description = "命中计费规则JSON")
private String billingRulesJson;
@Schema(description = "运输量")
private BigDecimal transportQuantity;
@@ -163,6 +163,9 @@ public class Waybill extends TenantEntity {
@Schema(description = "里程")
private BigDecimal mileage;
@Schema(description = "里程维护备注")
private String mileageRemark;
@Schema(description = "预计发货日期")
private LocalDate estimatedStartTime;
@@ -51,6 +51,10 @@ public class WaybillVO extends Waybill {
@Schema(description = "是否只读")
private Boolean readonly;
@TableField(exist = false)
@Schema(description = "是否允许维护里程")
private Boolean mileageMaintainable;
@TableField(exist = false)
@Schema(description = "创建人姓名")
private String createUserName;
@@ -11,6 +11,7 @@ import org.springblade.transport.pojo.dto.SettlementAdjustmentSaveRequest;
import org.springblade.transport.pojo.dto.SettlementAdjustmentStatusRequest;
import org.springblade.transport.pojo.entity.SettlementAdjustment;
import org.springblade.transport.pojo.vo.SettlementAdjustmentVO;
import org.springblade.transport.service.IPreSettlementService;
import org.springblade.transport.service.ISettlementAdjustmentService;
import org.springframework.web.bind.annotation.*;
@@ -23,10 +24,12 @@ import java.util.Map;
@RequestMapping("/settlement-adjustment")
public class SettlementAdjustmentController extends BladeController {
private final ISettlementAdjustmentService service;
private final IPreSettlementService preSettlementService;
@GetMapping("/list") public R<IPage<SettlementAdjustmentVO>> list(SettlementAdjustmentVO query, Query page) { return R.data(service.selectPage(Condition.getPage(page), query)); }
@GetMapping("/detail") public R<SettlementAdjustmentVO> detail(@RequestParam Long id) { return R.data(service.detail(id)); }
@GetMapping("/candidate-formal-settlements") public R<List<Map<String, Object>>> candidates(@RequestParam(required = false) String keyword) { return R.data(service.candidateFormalSettlements(keyword)); }
@GetMapping("/formal-details") public R<List<Map<String, Object>>> formalDetails(@RequestParam Long formalSettlementId) { return R.data(service.formalDetails(formalSettlementId)); }
@GetMapping("/fee-options") public R<List<Map<String, Object>>> feeOptions() { return R.data(preSettlementService.feeOptions()); }
@PostMapping("/save") public R<Long> save(@RequestBody SettlementAdjustmentSaveRequest request) { return R.data(service.saveDraft(request)); }
@PostMapping("/remove") public R remove(@RequestParam Long id) { service.removeDraft(id); return R.success("删除成功"); }
@PostMapping("/submit") public R submit(@RequestBody SettlementAdjustmentStatusRequest request) { service.submit(request); return R.success("提交成功"); }
@@ -54,6 +54,7 @@ import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.LoadingManageVO;
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
import org.springblade.transport.pojo.vo.WaybillVO;
import org.springblade.transport.pojo.dto.WaybillMileageRequest;
import org.springblade.transport.service.IContractManageService;
import org.springblade.transport.service.ICustomerArchiveService;
import org.springblade.transport.service.IProjectApplyService;
@@ -235,36 +236,43 @@ public class WaybillController extends BladeController {
return R.status(waybillService.changeRoute(waybill));
}
@PostMapping("/cancel")
@PostMapping("/maintain-mileage")
@ApiOperationSupport(order = 17)
@Operation(summary = "维护里程", description = "仅已完成且未生成结算单的运单允许维护")
public R maintainMileage(@RequestBody WaybillMileageRequest request) {
return R.status(waybillService.maintainMileage(request));
}
@PostMapping("/cancel")
@ApiOperationSupport(order = 18)
@Operation(summary = "取消", description = "传入id")
public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) {
return R.status(waybillService.cancel(id));
}
@PostMapping("/reassign")
@ApiOperationSupport(order = 18)
@ApiOperationSupport(order = 19)
@Operation(summary = "重新派单", description = "传入id")
public R reassign(@Parameter(description = "主键", required = true) @RequestParam Long id) {
return R.status(waybillService.reassign(id));
}
@PostMapping("/complete")
@ApiOperationSupport(order = 19)
@ApiOperationSupport(order = 20)
@Operation(summary = "完成", description = "传入id")
public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) {
return R.status(waybillService.complete(id));
}
@PostMapping("/batch-complete")
@ApiOperationSupport(order = 20)
@ApiOperationSupport(order = 21)
@Operation(summary = "批量完成", description = "传入ids")
public R<BusinessRemoveResultVO> batchComplete(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
return R.data(waybillService.batchComplete(ids));
}
@PostMapping("/road-loading")
@ApiOperationSupport(order = 21)
@ApiOperationSupport(order = 22)
@Operation(summary = "公路配载", description = "传入ids")
public R<LoadingManageVO> roadLoading(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
return R.data(waybillService.roadLoading(ids));
@@ -38,6 +38,8 @@ import org.springblade.transport.pojo.vo.ReceivablePayableFeeDetailVO;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.Set;
/**
* 应收应付明细服务
@@ -50,6 +52,8 @@ public interface IReceivablePayableDetailService extends BaseService<ReceivableP
List<ReceivablePayableDetailVO> selectList(ReceivablePayableDetailVO query);
Set<Long> settlementLinkedWaybillIds(Collection<Long> waybillIds);
ReceivablePayableFeeDetailVO feeDetail(Long id);
IPage<ReceivablePayableChangeRecordVO> changeRecords(IPage<?> page, Long detailId);
@@ -29,6 +29,7 @@ import org.springblade.transport.pojo.entity.Waybill;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.LoadingManageVO;
import org.springblade.transport.pojo.vo.WaybillVO;
import org.springblade.transport.pojo.dto.WaybillMileageRequest;
import java.util.List;
@@ -47,6 +48,7 @@ public interface IWaybillService extends BaseService<Waybill> {
List<WaybillExcel> importWaybill(List<WaybillExcel> data);
WaybillVO copy(Long id);
boolean changeRoute(Waybill waybill);
boolean maintainMileage(WaybillMileageRequest request);
boolean cancel(Long id);
boolean reassign(Long id);
boolean complete(Long id);
@@ -25,6 +25,8 @@ import org.springblade.transport.mapper.FormalSettlementInvoiceMapper;
import org.springblade.transport.mapper.FormalSettlementSourceMapper;
import org.springblade.transport.mapper.FormalSettlementSummaryFeeMapper;
import org.springblade.transport.mapper.FormalSettlementPaymentMapper;
import org.springblade.transport.mapper.InvoiceReceiptMapper;
import org.springblade.transport.mapper.InvoiceReceiptSettlementMapper;
import org.springblade.transport.mapper.PreSettlementDetailMapper;
import org.springblade.transport.mapper.PreSettlementDetailFeeMapper;
import org.springblade.transport.mapper.PreSettlementMapper;
@@ -48,6 +50,8 @@ import org.springblade.transport.pojo.entity.FormalSettlementInvoice;
import org.springblade.transport.pojo.entity.FormalSettlementSource;
import org.springblade.transport.pojo.entity.FormalSettlementSummaryFee;
import org.springblade.transport.pojo.entity.FormalSettlementPayment;
import org.springblade.transport.pojo.entity.InvoiceReceipt;
import org.springblade.transport.pojo.entity.InvoiceReceiptSettlement;
import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.entity.PreSettlement;
import org.springblade.transport.pojo.entity.PreSettlementDetail;
@@ -80,6 +84,7 @@ import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -108,6 +113,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
private final FormalSettlementSummaryFeeMapper summaryFeeMapper;
private final FormalSettlementPaymentMapper paymentMapper;
private final FormalSettlementInvoiceMapper invoiceMapper;
private final InvoiceReceiptMapper invoiceReceiptMapper;
private final InvoiceReceiptSettlementMapper invoiceReceiptSettlementMapper;
private final FormalSettlementChangeRecordMapper changeRecordMapper;
private final FormalSettlementDetailMapper detailMapper;
private final FormalSettlementDetailFeeMapper detailFeeMapper;
@@ -202,8 +209,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
vo.setSummaryFees(listSummaryFees(id));
vo.setPayments(paymentMapper.selectList(Wrappers.<FormalSettlementPayment>lambdaQuery()
.eq(FormalSettlementPayment::getFormalSettlementId, id).orderByDesc(FormalSettlementPayment::getCreateTime)));
vo.setInvoices(invoiceMapper.selectList(Wrappers.<FormalSettlementInvoice>lambdaQuery()
.eq(FormalSettlementInvoice::getFormalSettlementId, id).orderByAsc(FormalSettlementInvoice::getLineNo)));
vo.setInvoices(listInvoices(settlement));
List<Long> preSettlementIds = sources.stream().map(FormalSettlementSource::getPreSettlementId).toList();
LambdaQueryWrapper<PaymentApplication> paymentApplicationQuery = Wrappers.<PaymentApplication>lambdaQuery();
if (preSettlementIds.isEmpty()) {
@@ -225,6 +231,63 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
return vo;
}
private List<FormalSettlementInvoice> listInvoices(FormalSettlement settlement) {
List<FormalSettlementInvoice> settlementInvoices = invoiceMapper.selectList(
Wrappers.<FormalSettlementInvoice>lambdaQuery()
.eq(FormalSettlementInvoice::getFormalSettlementId, settlement.getId())
.orderByAsc(FormalSettlementInvoice::getLineNo));
Map<String, FormalSettlementInvoice> invoiceMap = new LinkedHashMap<>();
for (FormalSettlementInvoice invoice : settlementInvoices) {
invoiceMap.put(invoiceKey(invoice.getInvoiceNo(), "settlement:" + invoice.getId()), invoice);
}
List<InvoiceReceiptSettlement> receiptRelations = invoiceReceiptSettlementMapper.selectList(
Wrappers.<InvoiceReceiptSettlement>lambdaQuery()
.and(wrapper -> wrapper
.eq(InvoiceReceiptSettlement::getFormalSettlementId, settlement.getId())
.or()
.eq(InvoiceReceiptSettlement::getFormalSettlementNo, settlement.getFormalSettlementNo()))
.orderByAsc(InvoiceReceiptSettlement::getCreateTime));
if (!receiptRelations.isEmpty()) {
List<Long> receiptIds = receiptRelations.stream()
.map(InvoiceReceiptSettlement::getInvoiceReceiptId)
.filter(Objects::nonNull)
.distinct()
.toList();
Map<Long, InvoiceReceipt> receiptMap = receiptIds.isEmpty() ? Map.of()
: invoiceReceiptMapper.selectByIds(receiptIds).stream()
.filter(receipt -> !VOIDED.equals(receipt.getApprovalStatus())
&& !Objects.equals(receipt.getIsDeleted(), 1))
.collect(Collectors.toMap(InvoiceReceipt::getId, Function.identity()));
for (InvoiceReceiptSettlement relation : receiptRelations) {
InvoiceReceipt receipt = receiptMap.get(relation.getInvoiceReceiptId());
if (receipt == null) continue;
FormalSettlementInvoice invoice = new FormalSettlementInvoice();
invoice.setFormalSettlementId(settlement.getId());
invoice.setInvoiceNo(receipt.getInvoiceNo());
invoice.setInvoiceDate(receipt.getInvoiceDate());
invoice.setInvoiceType(receipt.getInvoiceType());
invoice.setTaxRate(receipt.getTaxRate());
invoice.setInvoiceAmount(money(receipt.getInvoiceAmount()));
invoice.setAvailableInvoiceAmount(money(receipt.getInvoiceAmount()));
invoice.setMatchedAmount(money(relation.getAllocatedInvoiceAmount()));
invoice.setAttachmentJson(receipt.getAttachmentsJson());
invoiceMap.put(invoiceKey(receipt.getInvoiceNo(), "receipt:" + receipt.getId()), invoice);
}
}
List<FormalSettlementInvoice> invoices = new ArrayList<>(invoiceMap.values());
for (int index = 0; index < invoices.size(); index++) {
invoices.get(index).setLineNo(index + 1);
}
return invoices;
}
private String invoiceKey(String invoiceNo, String fallback) {
String normalizedInvoiceNo = invoiceNo == null ? "" : invoiceNo.trim();
return normalizedInvoiceNo.isEmpty() ? fallback : normalizedInvoiceNo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long saveDraft(FormalSettlementSaveRequest request) {
@@ -282,6 +345,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
settlement.setRemark(limit(request.getRemark(), 200));
saveOrUpdate(settlement);
rebuildSnapshots(settlement, sources, directDetails);
applyDetailAdjustments(settlement.getId(), request.getDetailAdjustments());
rebuildSummaryFees(settlement.getId());
applySummaryRequest(settlement.getId(), request.getSummaryFees());
refreshSettlementAmount(settlement);
@@ -515,6 +579,75 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
renumberSummaryFees(settlementId);
}
private void applyDetailAdjustments(Long settlementId,
List<FormalSettlementSaveRequest.DetailAdjustment> requestRows) {
if (Func.isEmpty(requestRows)) return;
List<FormalSettlementDetail> details = detailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
.eq(FormalSettlementDetail::getFormalSettlementId, settlementId));
Map<Long, FormalSettlementDetail> preSettlementDetailMap = details.stream()
.filter(item -> item.getSourcePreSettlementDetailId() != null)
.collect(Collectors.toMap(FormalSettlementDetail::getSourcePreSettlementDetailId,
Function.identity(), (first, second) -> first));
Map<Long, FormalSettlementDetail> sourceDetailMap = details.stream()
.filter(item -> item.getSourcePreSettlementDetailId() == null && item.getSourceDetailId() != null)
.collect(Collectors.toMap(FormalSettlementDetail::getSourceDetailId,
Function.identity(), (first, second) -> first));
Set<String> adjustedDetailKeys = new LinkedHashSet<>();
for (FormalSettlementSaveRequest.DetailAdjustment requestRow : requestRows) {
if (requestRow == null) throw new ServiceException("存在无效的结算明细调整");
Long preSettlementDetailId = requestRow.getSourcePreSettlementDetailId();
Long sourceDetailId = requestRow.getSourceDetailId();
if (preSettlementDetailId != null && sourceDetailId != null) {
throw new ServiceException("结算明细调整只能指定一个来源明细");
}
String detailKey = preSettlementDetailId != null
? "pre:" + preSettlementDetailId : sourceDetailId == null ? null : "source:" + sourceDetailId;
if (detailKey == null || !adjustedDetailKeys.add(detailKey)) {
throw new ServiceException("结算明细调整数据无效或重复");
}
FormalSettlementDetail detail = preSettlementDetailId != null
? preSettlementDetailMap.get(preSettlementDetailId) : sourceDetailMap.get(sourceDetailId);
if (detail == null) throw new ServiceException("待调整的结算明细不属于当前正式结算单");
BigDecimal adjustAmount = money(requestRow.getAdjustAmount());
BigDecimal settlementAmount = money(detail.getOriginalAmount()).add(adjustAmount);
if (settlementAmount.signum() < 0) {
throw new ServiceException("单据" + detail.getDocumentNo() + "调整后的结算金额不能小于0");
}
applyDetailFeeAmount(detail, settlementAmount);
detail.setAdjustAmount(adjustAmount);
detail.setSettlementAmountTax(settlementAmount);
detailMapper.updateById(detail);
}
}
private void applyDetailFeeAmount(FormalSettlementDetail detail, BigDecimal settlementAmount) {
List<FormalSettlementDetailFee> fees = detailFees(detail.getId());
if (fees.isEmpty()) throw new ServiceException("正式结算明细费用不存在");
BigDecimal currentAmount = fees.stream().map(FormalSettlementDetailFee::getSettlementAmountTax)
.map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal difference = settlementAmount.subtract(currentAmount);
if (difference.signum() > 0) {
FormalSettlementDetailFee fee = fees.get(0);
fee.setSettlementAmountTax(money(fee.getSettlementAmountTax()).add(difference));
fee.setAdjustAmount(fee.getSettlementAmountTax().subtract(money(fee.getOriginalAmount())));
detailFeeMapper.updateById(fee);
return;
}
BigDecimal remainingDeduction = difference.abs();
for (FormalSettlementDetailFee fee : fees) {
if (remainingDeduction.signum() == 0) break;
BigDecimal currentFeeAmount = money(fee.getSettlementAmountTax());
BigDecimal deduction = currentFeeAmount.min(remainingDeduction);
fee.setSettlementAmountTax(currentFeeAmount.subtract(deduction));
fee.setAdjustAmount(fee.getSettlementAmountTax().subtract(money(fee.getOriginalAmount())));
detailFeeMapper.updateById(fee);
remainingDeduction = remainingDeduction.subtract(deduction);
}
if (remainingDeduction.signum() > 0) {
throw new ServiceException("结算明细调整后的金额无效");
}
}
private void appendFeeAggregates(Map<String, BigDecimal> aggregates, Map<String, String> feeTypeMap,
List<FormalSettlementDetailFee> detailFees) {
for (FormalSettlementDetailFee fee : detailFees) {
@@ -648,6 +781,9 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
invoice.setAttachmentJson(requestRow.getAttachmentJson());
invoiceMapper.insert(invoice);
}
settlement.setInvoiceAmount(money(matchedTotal));
settlement.setInvoiceStatus(invoiceStatus(matchedTotal, settlement.getSettlementAmount()));
updateById(settlement);
}
private void refreshSettlementAmount(FormalSettlement settlement) {
@@ -869,6 +1005,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
private FormalSettlementVO toVO(FormalSettlement entity) {
FormalSettlementVO vo = FormalSettlementWrapper.build().entityVO(entity);
vo.setInvoiceStatus(invoiceStatus(entity.getInvoiceAmount(), entity.getSettlementAmount()));
PaymentSummary summary = calculatePaymentSummary(entity);
vo.setAppliedPaymentAmount(summary.appliedAmount());
vo.setPaidAmount(summary.paidAmount());
@@ -951,6 +1088,12 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
return paidAmount.compareTo(money(settlementAmount)) >= 0 ? "paid" : "partial";
}
private String invoiceStatus(BigDecimal invoiceAmount, BigDecimal settlementAmount) {
BigDecimal matchedAmount = money(invoiceAmount);
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0) return "unreceived";
return matchedAmount.compareTo(money(settlementAmount)) == 0 ? "completed" : "partial";
}
private record PaymentSummary(BigDecimal appliedAmount, BigDecimal paidAmount) {
}
@@ -521,7 +521,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
.map(InvoiceApplicationSettlement::getAllocatedInvoiceAmount).map(this::money)
.reduce(BigDecimal.ZERO, BigDecimal::add);
String status = allocated.compareTo(BigDecimal.ZERO) <= 0 ? "unreceived"
: allocated.compareTo(money(settlement.getSettlementAmount())) >= 0 ? "completed" : "partial";
: allocated.compareTo(money(settlement.getSettlementAmount())) == 0 ? "completed" : "partial";
settlement.setInvoiceAmount(allocated);
settlement.setInvoiceStatus(status);
formalSettlementMapper.updateById(settlement);
@@ -500,7 +500,7 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
if (settlement == null) return;
BigDecimal invoiceAmount = receivedAmount(settlementId, null);
String invoiceStatus = invoiceAmount.compareTo(BigDecimal.ZERO) <= 0 ? "unreceived"
: invoiceAmount.compareTo(money(settlement.getSettlementAmount())) >= 0 ? "completed" : "partial";
: invoiceAmount.compareTo(money(settlement.getSettlementAmount())) == 0 ? "completed" : "partial";
settlement.setInvoiceAmount(invoiceAmount);
settlement.setInvoiceStatus(invoiceStatus);
formalSettlementMapper.updateById(settlement);
@@ -103,7 +103,17 @@ public class ReceivablePayableDetailServiceImpl
private static final String SOURCE_MASTER_ORDER = "总单系统生成";
private static final String SOURCE_LOADING_ORDER = "配载单系统生成";
private static final String FEE_SOURCE_AUTO = "自动生成";
private static final String FEE_SOURCE_MANUAL = "录入";
private static final String FEE_SOURCE_MANUAL = "录入";
private static final Set<String> FEE_SOURCE_MANUAL_LEGACY = Set.of("手工录入", "手动添加");
private static final Map<String, List<String>> MANUAL_BILLING_TYPES = Map.of(
"按重量", List.of("固定单价", "区间单价", "阶梯单价", "区间阶梯一口价"),
"按体积", List.of("固定单价", "区间单价", "阶梯单价", "区间阶梯一口价"),
"按车辆", List.of("固定单价"),
"按里程", List.of("固定单价", "区间单价", "阶梯单价", "区间阶梯一口价"),
"按吨·公里", List.of("固定单价", "区间单价", "阶梯单价", "区间阶梯一口价"),
"固定金额(整单一口价)", List.of("固定一口价"),
"按数量", List.of("固定单价", "区间单价", "阶梯单价", "区间阶梯一口价")
);
private final ReceivablePayableCargoFeeMapper cargoFeeMapper;
private final ReceivablePayableChangeRecordMapper changeRecordMapper;
@@ -148,6 +158,44 @@ public class ReceivablePayableDetailServiceImpl
return result;
}
@Override
public Set<Long> settlementLinkedWaybillIds(Collection<Long> waybillIds) {
Set<Long> candidateIds = waybillIds == null ? Set.of() : waybillIds.stream()
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
if (candidateIds.isEmpty()) {
return Set.of();
}
Set<Long> result = list(settlementLinkedQuery()
.in(ReceivablePayableDetail::getWaybillId, candidateIds))
.stream()
.map(ReceivablePayableDetail::getWaybillId)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
List<ReceivablePayableCargoFee> cargoFees = cargoFeeMapper.selectList(
Wrappers.<ReceivablePayableCargoFee>lambdaQuery()
.eq(ReceivablePayableCargoFee::getIsDeleted, 0)
.in(ReceivablePayableCargoFee::getWaybillId, candidateIds));
Set<Long> cargoDetailIds = cargoFees.stream()
.map(ReceivablePayableCargoFee::getDetailId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (cargoDetailIds.isEmpty()) {
return result;
}
Set<Long> settlementLinkedDetailIds = list(settlementLinkedQuery()
.in(ReceivablePayableDetail::getId, cargoDetailIds))
.stream()
.map(ReceivablePayableDetail::getId)
.collect(Collectors.toSet());
cargoFees.stream()
.filter(item -> settlementLinkedDetailIds.contains(item.getDetailId()))
.map(ReceivablePayableCargoFee::getWaybillId)
.filter(Objects::nonNull)
.forEach(result::add);
return result;
}
@Override
public ReceivablePayableFeeDetailVO feeDetail(Long id) {
ReceivablePayableDetail detail = getExisting(id);
@@ -282,11 +330,11 @@ public class ReceivablePayableDetailServiceImpl
throw new ServiceException("变更原因不能超过300个字");
}
if (Boolean.TRUE.equals(adjusted.getManualFee())) {
if (existing == null && !"receivable".equals(detail.getSettlementType())) {
throw new ServiceException("仅应明细允许新增手工费用");
if (existing == null && !"payable".equals(detail.getSettlementType())) {
throw new ServiceException("仅应明细允许新增费用");
}
if (existing != null && !isManualFee(existing)) {
throw new ServiceException("自动生成费用行不能变更为手录入");
throw new ServiceException("自动生成费用行不能变更为手录入");
}
validateAdjustRow(adjusted, true);
Map<String, BigDecimal> manualItems = validatedFeeItems(adjusted.getFeeItems(), allowedFeeItems);
@@ -304,8 +352,6 @@ public class ReceivablePayableDetailServiceImpl
BigDecimal oldAmount = money(existing.getAfterAmount());
applyEditableFields(existing, adjusted, true);
existing.setDataSource(FEE_SOURCE_MANUAL);
existing.setBillingFactor("-");
existing.setBillingType("-");
existing.setFreightAmount(freightAmount);
existing.setFeeItemsJson(JsonUtil.toJson(manualItems));
existing.setAdjustAmount(afterAmount.subtract(money(existing.getOriginalAmount())));
@@ -318,7 +364,7 @@ public class ReceivablePayableDetailServiceImpl
} else {
cargoFeeMapper.updateById(existing);
}
changes.add("【手工费用】从[" + Objects.toString(oldCargoName, "") + " "
changes.add("【手动录入】从[" + Objects.toString(oldCargoName, "") + " "
+ formatValue(oldAmount) + "]调整为[" + Objects.toString(existing.getCargoName(), "")
+ " " + formatValue(afterAmount) + "]");
continue;
@@ -327,7 +373,7 @@ public class ReceivablePayableDetailServiceImpl
throw new ServiceException("存在无效的费用调整行");
}
if (isManualFee(existing)) {
throw new ServiceException("录入费用行不能变更为自动生成");
throw new ServiceException("录入费用行不能变更为自动生成");
}
validateAdjustRow(adjusted, false);
Map<String, BigDecimal> feeItems = validatedFeeItems(adjusted.getFeeItems(), allowedFeeItems);
@@ -335,6 +381,14 @@ public class ReceivablePayableDetailServiceImpl
BigDecimal mileage = money(adjusted.getMileage());
BigDecimal freightAmount = money(adjusted.getFreightAmount());
Map<String, BigDecimal> effectiveFeeItems = feeItems;
boolean billingBasisChanged = money(existing.getTransportQuantity()).compareTo(transportQuantity) != 0
|| money(existing.getMileage()).compareTo(mileage) != 0;
if (billingBasisChanged) {
AdjustedFeeCalculation calculation = calculateAdjustedFee(detail, existing,
transportQuantity, mileage, freightAmount, feeItems);
freightAmount = calculation.freightAmount();
effectiveFeeItems = calculation.feeItems();
}
appendChange(changes, "规格", existing.getSpecification(), adjusted.getSpecification());
appendChange(changes, "型号", existing.getModel(), adjusted.getModel());
appendChange(changes, "计费要素", existing.getBillingFactor(), adjusted.getBillingFactor());
@@ -983,6 +1037,17 @@ public class ReceivablePayableDetailServiceImpl
return wrapper.orderByDesc(ReceivablePayableDetail::getCreateTime);
}
private LambdaQueryWrapper<ReceivablePayableDetail> settlementLinkedQuery() {
return Wrappers.<ReceivablePayableDetail>lambdaQuery()
.eq(ReceivablePayableDetail::getIsDeleted, 0)
.and(wrapper -> wrapper
.isNotNull(ReceivablePayableDetail::getPreSettlementNo)
.ne(ReceivablePayableDetail::getPreSettlementNo, "")
.or()
.isNotNull(ReceivablePayableDetail::getFormalSettlementNo)
.ne(ReceivablePayableDetail::getFormalSettlementNo, ""));
}
private LambdaQueryWrapper<ReceivablePayableDetail> buildUpdateQuery(ReceivablePayableUpdateFeeRequest request) {
LambdaQueryWrapper<ReceivablePayableDetail> wrapper = Wrappers.<ReceivablePayableDetail>lambdaQuery()
.eq(ReceivablePayableDetail::getIsDeleted, 0)
@@ -1276,6 +1341,7 @@ public class ReceivablePayableDetailServiceImpl
}
Map<List<String>, ReceivablePayableCargoFee> feesByCargo = new LinkedHashMap<>();
Map<List<String>, Map<String, BigDecimal>> feeItemsByCargo = new LinkedHashMap<>();
Map<List<String>, List<Map<String, Object>>> billingRulesByCargo = new LinkedHashMap<>();
Set<List<String>> freightBillingCargoKeys = new LinkedHashSet<>();
for (Object value : (List<?>) plan.get("rules")) {
if (!(value instanceof Map<?, ?> raw)) continue;
@@ -1292,8 +1358,12 @@ public class ReceivablePayableDetailServiceImpl
key -> buildCalculatedCargoFee(waybill, feeWaybill, feeGoods, rule));
Map<String, BigDecimal> feeItems = feeItemsByCargo.computeIfAbsent(cargoKey,
key -> new LinkedHashMap<>());
List<Map<String, Object>> billingRules = billingRulesByCargo.computeIfAbsent(cargoKey,
key -> new ArrayList<>());
billingRules.add(new LinkedHashMap<>(rule));
feeItems.merge(feeItem, amount, BigDecimal::add);
fee.setFeeItemsJson(JsonUtil.toJson(feeItems));
fee.setBillingRulesJson(JsonUtil.toJson(billingRules));
fee.setOriginalAmount(money(fee.getOriginalAmount()).add(amount));
fee.setAfterAmount(fee.getOriginalAmount());
if (isFreightRule(rule)) {
@@ -1648,13 +1718,25 @@ public class ReceivablePayableDetailServiceImpl
}
private boolean isManualFee(ReceivablePayableCargoFee fee) {
return FEE_SOURCE_MANUAL.equals(fee.getDataSource()) || "手工调整".equals(fee.getBillingFactor());
return FEE_SOURCE_MANUAL.equals(fee.getDataSource())
|| FEE_SOURCE_MANUAL_LEGACY.contains(fee.getDataSource())
|| "手工调整".equals(fee.getBillingFactor());
}
private void validateAdjustRow(ReceivablePayableAdjustFeeRequest.AdjustRow row, boolean manualFee) {
if (manualFee) {
if (isBlank(row.getCargoName())) {
throw new ServiceException("货物名称不能为空");
}
validateLength(row.getCargoName(), 100, "货物名称");
validateLength(row.getCargoType(), 100, "货物类型");
List<String> billingTypes = MANUAL_BILLING_TYPES.get(row.getBillingFactor());
if (billingTypes == null) {
throw new ServiceException("请选择计费要素");
}
if (!billingTypes.contains(row.getBillingType())) {
throw new ServiceException("请选择计费要素对应的计费类型");
}
}
validateLength(row.getSpecification(), 255, "规格");
validateLength(row.getModel(), 255, "型号");
@@ -1688,10 +1770,10 @@ public class ReceivablePayableDetailServiceImpl
}
private void applyEditableFields(ReceivablePayableCargoFee fee,
ReceivablePayableAdjustFeeRequest.AdjustRow adjusted,
boolean manualFee) {
ReceivablePayableAdjustFeeRequest.AdjustRow adjusted,
boolean manualFee) {
if (manualFee) {
fee.setCargoName(adjusted.getCargoName());
fee.setCargoName(adjusted.getCargoName().trim());
fee.setCargoType(adjusted.getCargoType());
}
fee.setSpecification(adjusted.getSpecification());
@@ -1727,12 +1809,18 @@ public class ReceivablePayableDetailServiceImpl
if (isManualFee(fee)) {
throw new ServiceException("手工费用不支持按合同计费规则试算");
}
ContractManage contract = contractManageService.getById(detail.getContractId());
if (contract == null) {
throw new ServiceException("关联合同不存在");
}
Waybill adjustedWaybill = adjustedWaybill(detail, fee, transportQuantity, mileage);
List<Map<String, Object>> rules = matchingAdjustedRules(contract, fee, adjustedWaybill);
List<Map<String, Object>> rules = parseList(fee.getBillingRulesJson());
if (rules.isEmpty()) {
ContractManage contract = contractManageService.getById(detail.getContractId());
if (contract == null) {
throw new ServiceException("关联合同不存在");
}
rules = matchingAdjustedRules(contract, fee, adjustedWaybill);
if (!rules.isEmpty()) {
fee.setBillingRulesJson(JsonUtil.toJson(rules));
}
}
if (rules.isEmpty()) {
throw new ServiceException("未找到费用明细对应的合同计费规则,请先更新费用");
}
@@ -1760,7 +1848,8 @@ public class ReceivablePayableDetailServiceImpl
private Waybill adjustedWaybill(ReceivablePayableDetail detail, ReceivablePayableCargoFee fee,
BigDecimal transportQuantity, BigDecimal mileage) {
Waybill source = detail.getWaybillId() == null ? null : waybillService.getById(detail.getWaybillId());
Long waybillId = fee.getWaybillId() == null ? detail.getWaybillId() : fee.getWaybillId();
Waybill source = waybillId == null ? null : waybillService.getById(waybillId);
Waybill waybill = source == null ? new Waybill()
: Objects.requireNonNull(BeanUtil.copyProperties(source, Waybill.class));
waybill.setQuantity(transportQuantity);
@@ -1791,15 +1880,23 @@ public class ReceivablePayableDetailServiceImpl
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<>();
for (Object value : rules) {
if (!(value instanceof Map<?, ?> raw)) continue;
Map<String, Object> rule = new LinkedHashMap<>();
raw.forEach((key, item) -> rule.put(String.valueOf(key), item));
if (!feeItemNames.contains(stringValue(rule, "feeItem")) || !matchesRule(rule, waybill)) continue;
candidates.add(rule);
if (!feeItemNames.contains(stringValue(rule, "feeItem"))) continue;
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;
@@ -1811,6 +1908,7 @@ public class ReceivablePayableDetailServiceImpl
}
}
if (!billingMatchedCandidates.isEmpty()) return billingMatchedCandidates;
if (!billingFieldCandidates.isEmpty()) return billingFieldCandidates;
return defaultCandidates.isEmpty() ? firstCandidates : defaultCandidates;
}
@@ -13,6 +13,7 @@ import org.springblade.transport.mapper.FormalSettlementDetailFeeMapper;
import org.springblade.transport.mapper.FormalSettlementDetailMapper;
import org.springblade.transport.mapper.FormalSettlementChangeRecordMapper;
import org.springblade.transport.mapper.FormalSettlementMapper;
import org.springblade.transport.mapper.FormalSettlementSummaryFeeMapper;
import org.springblade.transport.mapper.SettlementAdjustmentDetailMapper;
import org.springblade.transport.mapper.SettlementAdjustmentMapper;
import org.springblade.transport.pojo.dto.SettlementAdjustmentSaveRequest;
@@ -21,6 +22,7 @@ import org.springblade.transport.pojo.entity.FormalSettlement;
import org.springblade.transport.pojo.entity.FormalSettlementChangeRecord;
import org.springblade.transport.pojo.entity.FormalSettlementDetail;
import org.springblade.transport.pojo.entity.FormalSettlementDetailFee;
import org.springblade.transport.pojo.entity.FormalSettlementSummaryFee;
import org.springblade.transport.pojo.entity.SettlementAdjustment;
import org.springblade.transport.pojo.entity.SettlementAdjustmentDetail;
import org.springblade.transport.pojo.vo.SettlementAdjustmentVO;
@@ -46,10 +48,12 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
private static final String REVIEWING = "reviewing";
private static final String APPROVED = "approved";
private static final String RETURNED = "returned";
private static final String PAID = "paid";
private final SettlementAdjustmentDetailMapper detailMapper;
private final FormalSettlementMapper formalMapper;
private final FormalSettlementDetailMapper formalDetailMapper;
private final FormalSettlementDetailFeeMapper formalFeeMapper;
private final FormalSettlementSummaryFeeMapper formalSummaryFeeMapper;
private final FormalSettlementChangeRecordMapper formalChangeRecordMapper;
@Override
@@ -86,6 +90,8 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
public List<Map<String, Object>> candidateFormalSettlements(String keyword) {
List<FormalSettlement> rows = formalMapper.selectList(Wrappers.<FormalSettlement>lambdaQuery()
.eq(FormalSettlement::getApprovalStatus, APPROVED)
.and(wrapper -> wrapper.isNull(FormalSettlement::getPaymentStatus)
.or().ne(FormalSettlement::getPaymentStatus, PAID))
.like(Func.isNotEmpty(keyword), FormalSettlement::getFormalSettlementNo, keyword)
.orderByDesc(FormalSettlement::getCreateTime));
List<Map<String, Object>> result = new ArrayList<>();
@@ -104,8 +110,7 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
@Override
public List<Map<String, Object>> formalDetails(Long formalSettlementId) {
FormalSettlement settlement = formalMapper.selectById(formalSettlementId);
if (settlement == null || !APPROVED.equals(settlement.getApprovalStatus())) throw new ServiceException("仅审批通过的正式结算单可调整");
FormalSettlement settlement = adjustableFormalSettlement(formalSettlementId);
List<Map<String, Object>> result = new ArrayList<>();
List<FormalSettlementDetail> details = formalDetailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
.eq(FormalSettlementDetail::getFormalSettlementId, formalSettlementId).orderByAsc(FormalSettlementDetail::getLineNo));
@@ -126,38 +131,64 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
@Override
@Transactional(rollbackFor = Exception.class)
public Long saveDraft(SettlementAdjustmentSaveRequest request) {
if (request.getFormalSettlementId() == null) throw new ServiceException("请选择关联正式结算单");
if (Func.isEmpty(request.getDetails())) throw new ServiceException("请至少添加一条调整费用");
FormalSettlement formal = formalMapper.selectById(request.getFormalSettlementId());
if (formal == null || !APPROVED.equals(formal.getApprovalStatus())) throw new ServiceException("仅审批通过的正式结算单可调整");
SettlementAdjustment adjustment = request.getId() == null ? new SettlementAdjustment() : editable(request.getId());
if (adjustment.getId() != null) detailMapper.delete(Wrappers.<SettlementAdjustmentDetail>lambdaQuery().eq(SettlementAdjustmentDetail::getAdjustmentId, adjustment.getId()));
if (adjustment.getId() == null) { adjustment.setAdjustmentNo(nextNo()); adjustment.setApprovalStatus(DRAFT); }
adjustment.setFormalSettlementId(formal.getId()); adjustment.setFormalSettlementNo(formal.getFormalSettlementNo());
adjustment.setSettlementType(formal.getSettlementType()); adjustment.setProjectName(formal.getProjectName());
adjustment.setDeptName(formal.getDeptName()); adjustment.setCustomerName("receivable".equals(formal.getSettlementType()) ? formal.getPayerName() : formal.getPayeeName());
adjustment.setContractNo(formal.getContractNo()); adjustment.setContractName(formal.getContractName());
adjustment.setKingdeeSyncStatus(formal.getKingdeeSyncStatus());
adjustment.setOriginalSettlementAmount(money(formal.getSettlementAmount())); adjustment.setRemark(limit(request.getRemark(), 200));
FormalSettlement formal = request.getFormalSettlementId() == null
? null : formalMapper.selectById(request.getFormalSettlementId());
if (adjustment.getId() == null) adjustment.setAdjustmentNo(nextNo());
adjustment.setApprovalStatus(DRAFT);
adjustment.setCurrentNode("草稿");
adjustment.setCurrentProcessor(AuthUtil.getUserName());
adjustment.setApprovedTime(null);
applyFormalSnapshot(adjustment, formal, request.getFormalSettlementId());
adjustment.setRemark(request.getRemark());
adjustment.setAttachmentsJson(request.getAttachmentsJson());
adjustment.setAdjustmentAmount(BigDecimal.ZERO.setScale(2));
adjustment.setAdjustedSettlementAmount(money(adjustment.getOriginalSettlementAmount()));
saveOrUpdate(adjustment);
detailMapper.delete(Wrappers.<SettlementAdjustmentDetail>lambdaQuery()
.eq(SettlementAdjustmentDetail::getAdjustmentId, adjustment.getId()));
BigDecimal total = BigDecimal.ZERO;
if (request.getDetails() != null) for (SettlementAdjustmentSaveRequest.Detail row : request.getDetails()) {
FormalSettlementDetailFee fee = validateFee(formal.getId(), row.getFormalSettlementDetailId(), row.getFormalSettlementDetailFeeId());
if (adjustment.getId() == null) save(adjustment);
if (row == null) continue;
SettlementAdjustmentDetail detail = new SettlementAdjustmentDetail(); detail.setAdjustmentId(adjustment.getId());
detail.setFormalSettlementDetailId(row.getFormalSettlementDetailId()); detail.setFormalSettlementDetailFeeId(row.getFormalSettlementDetailFeeId());
detail.setFeeType(row.getFeeType()); detail.setFeeItem(row.getFeeItem()); detail.setOriginalAmountTax(fee.getSettlementAmountTax());
detail.setAdjustmentAmountTax(row.getAdjustmentAmountTax() == null ? BigDecimal.ZERO : row.getAdjustmentAmountTax());
detail.setAdjustmentAmountNoTax(row.getAdjustmentAmountNoTax()); detail.setRemark(limit(row.getRemark(), 200)); detailMapper.insert(detail);
detail.setFeeType(row.getFeeType()); detail.setFeeItem(row.getFeeItem());
detail.setOriginalAmountTax(money(row.getOriginalAmountTax()));
detail.setAdjustmentAmountTax(money(row.getAdjustmentAmountTax()));
detail.setAdjustmentAmountNoTax(row.getAdjustmentAmountNoTax()); detail.setRemark(row.getRemark()); detailMapper.insert(detail);
total = total.add(detail.getAdjustmentAmountTax());
}
adjustment.setAdjustmentAmount(total); adjustment.setAdjustedSettlementAmount(adjustment.getOriginalSettlementAmount().add(total)); saveOrUpdate(adjustment);
adjustment.setAdjustmentAmount(total); adjustment.setAdjustedSettlementAmount(money(adjustment.getOriginalSettlementAmount()).add(total)); updateById(adjustment);
return adjustment.getId();
}
@Override @Transactional(rollbackFor = Exception.class)
public void removeDraft(Long id) { SettlementAdjustment item = editable(id); detailMapper.delete(Wrappers.<SettlementAdjustmentDetail>lambdaQuery().eq(SettlementAdjustmentDetail::getAdjustmentId, id)); removeById(item.getId()); }
@Override public void submit(SettlementAdjustmentStatusRequest request) { changeStatus(request.getId(), DRAFT, REVIEWING, "审批中", null); }
@Override
@Transactional(rollbackFor = Exception.class)
public void submit(SettlementAdjustmentStatusRequest request) {
SettlementAdjustment item = existing(request.getId());
if (!DRAFT.equals(item.getApprovalStatus())) throw new ServiceException("仅草稿状态的调整单允许提交");
FormalSettlement formal = adjustableFormalSettlement(item.getFormalSettlementId());
List<SettlementAdjustmentDetail> details = detailMapper.selectList(
Wrappers.<SettlementAdjustmentDetail>lambdaQuery()
.eq(SettlementAdjustmentDetail::getAdjustmentId, item.getId()));
if (details.isEmpty()) throw new ServiceException("请至少添加一条调整费用");
for (SettlementAdjustmentDetail detail : details) {
boolean manualFee = isManualFee(detail);
if (!manualFee && (detail.getFormalSettlementDetailId() == null
|| detail.getFormalSettlementDetailFeeId() == null)) {
throw new ServiceException("费用明细关联信息不完整");
}
if (!manualFee) validateFee(formal.getId(), detail.getFormalSettlementDetailId(),
detail.getFormalSettlementDetailFeeId());
requiredText(detail.getFeeType(), "费用类型", 100);
requiredText(detail.getFeeItem(), "费用项目", 100);
limit(detail.getRemark(), 200);
}
limit(item.getRemark(), 200);
changeStatus(item.getId(), DRAFT, REVIEWING, "审批中", null);
}
@Override public void returnBill(SettlementAdjustmentStatusRequest request) { changeStatus(request.getId(), REVIEWING, RETURNED, "已驳回", limit(request.getReason(), 200)); }
@Override
@@ -177,19 +208,26 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
public void approve(SettlementAdjustmentStatusRequest request) {
SettlementAdjustment adjustment = existing(request.getId());
if (!REVIEWING.equals(adjustment.getApprovalStatus())) throw new ServiceException("仅审批中的结算调整单允许审核");
FormalSettlement formal = formalMapper.selectById(adjustment.getFormalSettlementId());
if (formal == null || !APPROVED.equals(formal.getApprovalStatus())) throw new ServiceException("关联正式结算单状态已变化,无法审批");
FormalSettlement formal = adjustableFormalSettlement(adjustment.getFormalSettlementId());
for (SettlementAdjustmentDetail item : detailMapper.selectList(Wrappers.<SettlementAdjustmentDetail>lambdaQuery().eq(SettlementAdjustmentDetail::getAdjustmentId, adjustment.getId()))) {
FormalSettlementDetailFee fee = validateFee(formal.getId(), item.getFormalSettlementDetailId(), item.getFormalSettlementDetailFeeId());
BigDecimal beforeAmount = money(fee.getSettlementAmountTax());
fee.setSettlementAmountTax(money(fee.getSettlementAmountTax()).add(money(item.getAdjustmentAmountTax())));
if (item.getAdjustmentAmountNoTax() != null) fee.setSettlementAmountNoTax(money(fee.getSettlementAmountNoTax()).add(item.getAdjustmentAmountNoTax()));
fee.setAdjustAmount(money(fee.getAdjustAmount()).add(item.getAdjustmentAmountTax())); formalFeeMapper.updateById(fee);
FormalSettlementDetail formalDetail = formalDetailMapper.selectById(item.getFormalSettlementDetailId());
saveFormalChange(formal.getId(), "结算明细项", formalDetail == null ? null : formalDetail.getLineNo(),
"调整", "" + (Func.isEmpty(item.getFeeItem()) ? "结算费用" : item.getFeeItem())
+ "】从【" + beforeAmount + "】调整为【" + fee.getSettlementAmountTax() + "",
Func.isEmpty(item.getRemark()) ? adjustment.getRemark() : item.getRemark());
if (money(item.getAdjustmentAmountTax()).signum() == 0) continue;
if (isManualFee(item)) {
saveFormalChange(formal.getId(), "合计费用项", null, "调整",
"新增【" + item.getFeeItem() + "】调整费用【" + money(item.getAdjustmentAmountTax()) + "",
Func.isEmpty(item.getRemark()) ? adjustment.getRemark() : item.getRemark());
} else {
FormalSettlementDetailFee fee = validateFee(formal.getId(), item.getFormalSettlementDetailId(), item.getFormalSettlementDetailFeeId());
BigDecimal beforeAmount = money(fee.getSettlementAmountTax());
fee.setSettlementAmountTax(beforeAmount.add(money(item.getAdjustmentAmountTax())));
if (item.getAdjustmentAmountNoTax() != null) fee.setSettlementAmountNoTax(money(fee.getSettlementAmountNoTax()).add(item.getAdjustmentAmountNoTax()));
fee.setAdjustAmount(money(fee.getAdjustAmount()).add(item.getAdjustmentAmountTax())); formalFeeMapper.updateById(fee);
FormalSettlementDetail formalDetail = formalDetailMapper.selectById(item.getFormalSettlementDetailId());
saveFormalChange(formal.getId(), "结算明细项", formalDetail == null ? null : formalDetail.getLineNo(),
"调整", "" + (Func.isEmpty(item.getFeeItem()) ? "结算费用" : item.getFeeItem())
+ "】从【" + beforeAmount + "】调整为【" + fee.getSettlementAmountTax() + "",
Func.isEmpty(item.getRemark()) ? adjustment.getRemark() : item.getRemark());
}
applySummaryAdjustment(formal.getId(), item);
}
for (FormalSettlementDetail detail : formalDetailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery().eq(FormalSettlementDetail::getFormalSettlementId, formal.getId()))) {
List<FormalSettlementDetailFee> fees = formalFeeMapper.selectList(Wrappers.<FormalSettlementDetailFee>lambdaQuery().eq(FormalSettlementDetailFee::getFormalSettlementDetailId, detail.getId()));
@@ -197,8 +235,12 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
detail.setSettlementAmountNoTax(fees.stream().map(FormalSettlementDetailFee::getSettlementAmountNoTax).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
detail.setAdjustAmount(detail.getSettlementAmountTax().subtract(money(detail.getOriginalAmount()))); formalDetailMapper.updateById(detail);
}
BigDecimal amount = formalDetailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery().eq(FormalSettlementDetail::getFormalSettlementId, formal.getId())).stream().map(FormalSettlementDetail::getSettlementAmountTax).map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
formal.setSettlementAmount(amount); formal.setLocalSettlementAmount(amount.multiply(formal.getExchangeRate() == null ? BigDecimal.ONE : formal.getExchangeRate())); formalMapper.updateById(formal);
BigDecimal amount = money(formal.getSettlementAmount()).add(money(adjustment.getAdjustmentAmount()));
formal.setSettlementAmount(amount);
formal.setLocalSettlementAmount(amount.multiply(formal.getExchangeRate() == null ? BigDecimal.ONE : formal.getExchangeRate()));
formal.setRemainingPayableAmount(amount.subtract(money(formal.getAppliedPaymentAmount()))
.subtract(money(formal.getPaidAmount())).max(BigDecimal.ZERO));
formalMapper.updateById(formal);
saveFormalChange(formal.getId(), "合计费用项", null, "调整",
"调整单" + adjustment.getAdjustmentNo() + "调整金额【" + money(adjustment.getAdjustmentAmount()) + "",
adjustment.getRemark());
@@ -222,11 +264,66 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
private void changeStatus(Long id, String from, String to, String node, String reason) { SettlementAdjustment item = existing(id); if (!from.equals(item.getApprovalStatus())) throw new ServiceException("当前状态不允许此操作"); item.setApprovalStatus(to); item.setCurrentNode(node); if (reason != null) item.setRemark(reason); updateById(item); }
private SettlementAdjustment existing(Long id) { SettlementAdjustment item = getById(id); if (item == null || Objects.equals(item.getIsDeleted(), 1)) throw new ServiceException("结算调整单不存在"); return item; }
private SettlementAdjustment editable(Long id) { SettlementAdjustment item = existing(id); if (!(DRAFT.equals(item.getApprovalStatus()) || RETURNED.equals(item.getApprovalStatus()))) throw new ServiceException("仅草稿或驳回的调整单可编辑"); return item; }
private void applyFormalSnapshot(SettlementAdjustment adjustment, FormalSettlement formal, Long formalSettlementId) {
adjustment.setFormalSettlementId(formalSettlementId);
if (formal == null) {
adjustment.setFormalSettlementNo(null); adjustment.setSettlementType(null);
adjustment.setProjectName(null); adjustment.setDeptName(null); adjustment.setCustomerName(null);
adjustment.setContractNo(null); adjustment.setContractName(null); adjustment.setKingdeeSyncStatus(null);
adjustment.setOriginalSettlementAmount(BigDecimal.ZERO.setScale(2));
return;
}
adjustment.setFormalSettlementId(formal.getId()); adjustment.setFormalSettlementNo(formal.getFormalSettlementNo());
adjustment.setSettlementType(formal.getSettlementType()); adjustment.setProjectName(formal.getProjectName());
adjustment.setDeptName(formal.getDeptName()); adjustment.setCustomerName("receivable".equals(formal.getSettlementType()) ? formal.getPayerName() : formal.getPayeeName());
adjustment.setContractNo(formal.getContractNo()); adjustment.setContractName(formal.getContractName());
adjustment.setKingdeeSyncStatus(formal.getKingdeeSyncStatus()); adjustment.setOriginalSettlementAmount(money(formal.getSettlementAmount()));
}
private FormalSettlement adjustableFormalSettlement(Long formalSettlementId) {
if (formalSettlementId == null) throw new ServiceException("关联正式结算单不存在");
FormalSettlement formal = formalMapper.selectById(formalSettlementId);
if (formal == null || !APPROVED.equals(formal.getApprovalStatus())) {
throw new ServiceException("仅审批通过的正式结算单可调整");
}
if (PAID.equals(formal.getPaymentStatus())) {
throw new ServiceException("该正式结算单已完成收/付款,不能进行结算调整");
}
return formal;
}
private boolean isManualFee(SettlementAdjustmentSaveRequest.Detail row) { return row.getFormalSettlementDetailId() == null && row.getFormalSettlementDetailFeeId() == null; }
private boolean isManualFee(SettlementAdjustmentDetail row) { return row.getFormalSettlementDetailId() == null && row.getFormalSettlementDetailFeeId() == null; }
private FormalSettlementDetailFee validateFee(Long formalId, Long detailId, Long feeId) { FormalSettlementDetail detail = formalDetailMapper.selectById(detailId); FormalSettlementDetailFee fee = formalFeeMapper.selectById(feeId); if (detail == null || fee == null || !Objects.equals(detail.getFormalSettlementId(), formalId) || !Objects.equals(fee.getFormalSettlementDetailId(), detailId)) throw new ServiceException("费用明细不存在或不属于关联正式结算单"); return fee; }
private List<FormalSettlementSummaryFee> formalSummaryFees(Long formalId) { return formalSummaryFeeMapper.selectList(Wrappers.<FormalSettlementSummaryFee>lambdaQuery().eq(FormalSettlementSummaryFee::getFormalSettlementId, formalId).eq(FormalSettlementSummaryFee::getIsDeleted, 0).orderByAsc(FormalSettlementSummaryFee::getLineNo)); }
private void applySummaryAdjustment(Long formalId, SettlementAdjustmentDetail detail) {
List<FormalSettlementSummaryFee> summaryFees = formalSummaryFees(formalId);
FormalSettlementSummaryFee summaryFee = summaryFees.stream()
.filter(item -> Objects.equals(item.getFeeType(), detail.getFeeType()) && Objects.equals(item.getFeeItem(), detail.getFeeItem()))
.findFirst().orElse(null);
BigDecimal adjustmentAmount = money(detail.getAdjustmentAmountTax());
if (summaryFee == null) {
summaryFee = new FormalSettlementSummaryFee();
summaryFee.setFormalSettlementId(formalId);
summaryFee.setLineNo(summaryFees.stream().map(FormalSettlementSummaryFee::getLineNo)
.filter(Objects::nonNull).max(Integer::compareTo).orElse(0) + 1);
summaryFee.setFeeType(detail.getFeeType());
summaryFee.setFeeItem(detail.getFeeItem());
summaryFee.setOriginalAmount(BigDecimal.ZERO.setScale(2));
summaryFee.setAdjustAmount(adjustmentAmount);
summaryFee.setSettlementAmount(adjustmentAmount);
summaryFee.setRemark("");
summaryFee.setManualFlag(1);
formalSummaryFeeMapper.insert(summaryFee);
return;
}
summaryFee.setAdjustAmount(money(summaryFee.getAdjustAmount()).add(adjustmentAmount));
summaryFee.setSettlementAmount(money(summaryFee.getSettlementAmount()).add(adjustmentAmount));
formalSummaryFeeMapper.updateById(summaryFee);
}
private SettlementAdjustmentVO toVO(SettlementAdjustment item) { SettlementAdjustmentVO vo = new SettlementAdjustmentVO(); org.springframework.beans.BeanUtils.copyProperties(item, vo); vo.setCreateUserName(UserCache.getUserRealName(item.getCreateUser())); vo.setApprovalStatusName(statusName(item.getApprovalStatus())); vo.setSettlementTypeName(typeName(item.getSettlementType())); return vo; }
private String statusName(String value) { return Map.of(DRAFT, "草稿", REVIEWING, "审批中", APPROVED, "审批通过", RETURNED, "已驳回").getOrDefault(value, value); }
private String typeName(String value) { return "receivable".equals(value) ? "应收" : "应付"; }
private String typeName(String value) { return "receivable".equals(value) ? "应收" : "payable".equals(value) ? "应付" : ""; }
private synchronized String nextNo() { String prefix = "TZ" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); long count = count(Wrappers.<SettlementAdjustment>lambdaQuery().likeRight(SettlementAdjustment::getAdjustmentNo, prefix)); return prefix + String.format("%04d", count + 1); }
private BigDecimal money(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; }
private String requiredText(String value, String field, int max) { if (Func.isEmpty(value)) throw new ServiceException(field + "不能为空"); return limit(value.trim(), max); }
private String limit(String value, int max) { if (value != null && value.length() > max) throw new ServiceException("内容不能超过" + max + "个字"); return value; }
}
@@ -40,6 +40,7 @@ import org.springblade.transport.pojo.entity.LoadingManage;
import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.entity.ProcessConfig;
import org.springblade.transport.pojo.entity.Waybill;
import org.springblade.transport.pojo.dto.WaybillMileageRequest;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.LoadingManageVO;
import org.springblade.transport.pojo.vo.WaybillVO;
@@ -60,6 +61,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -89,12 +91,16 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
@Override
public IPage<WaybillVO> selectWaybillPage(IPage<Waybill> page, WaybillVO waybill) {
IPage<Waybill> entityPage = page(page, buildQuery(waybill));
return WaybillWrapper.build().pageVO(entityPage);
IPage<WaybillVO> result = WaybillWrapper.build().pageVO(entityPage);
fillMileageMaintainable(result.getRecords());
return result;
}
@Override
public WaybillVO detail(Long id) {
return WaybillWrapper.build().entityVO(loadEditable(id, false));
WaybillVO result = WaybillWrapper.build().entityVO(loadEditable(id, false));
fillMileageMaintainable(List.of(result));
return result;
}
@Override
@@ -108,8 +114,10 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
waybill.setLoadingNo(oldRecord.getLoadingNo());
waybill.setDeptId(oldRecord.getDeptId());
waybill.setDeptName(oldRecord.getDeptName());
waybill.setMileageRemark(oldRecord.getMileageRemark());
} else {
waybill.setLoadingNo(null);
waybill.setMileageRemark(null);
fillProjectProcessConfig(waybill);
}
prepare(waybill);
@@ -306,6 +314,32 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
return updateById(oldRecord);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean maintainMileage(WaybillMileageRequest request) {
if (request == null || request.getId() == null) {
throw new ServiceException("运单里程维护数据不能为空");
}
Waybill waybill = loadEditable(request.getId(), true);
if (!"completed".equals(waybill.getBusinessStatus())) {
throw new ServiceException("仅已完成运单允许维护里程");
}
if (receivablePayableDetailService.settlementLinkedWaybillIds(List.of(waybill.getId()))
.contains(waybill.getId())) {
throw new ServiceException("该运单已生成结算单,无法维护里程");
}
BigDecimal mileage = request.getMileage();
if (mileage == null || mileage.compareTo(BigDecimal.ZERO) <= 0
|| mileage.stripTrailingZeros().scale() > 0 || mileage.stripTrailingZeros().precision() > 10) {
throw new ServiceException("里程必须为不超过10位的正整数");
}
String mileageRemark = TransportBusinessSupport.trimToNull(request.getMileageRemark());
TransportBusinessSupport.validateLength(mileageRemark, 200, "里程维护备注不能超过200字");
waybill.setMileage(mileage);
waybill.setMileageRemark(mileageRemark);
return updateById(waybill);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancel(Long id) {
@@ -504,6 +538,22 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
return queryWrapper;
}
private void fillMileageMaintainable(List<WaybillVO> waybills) {
waybills.forEach(item -> item.setMileageMaintainable(false));
List<Long> completedIds = waybills.stream()
.filter(item -> "completed".equals(item.getBusinessStatus()))
.map(Waybill::getId)
.filter(Objects::nonNull)
.toList();
if (completedIds.isEmpty()) {
return;
}
Set<Long> settlementLinkedIds = receivablePayableDetailService.settlementLinkedWaybillIds(completedIds);
waybills.stream()
.filter(item -> completedIds.contains(item.getId()))
.forEach(item -> item.setMileageMaintainable(!settlementLinkedIds.contains(item.getId())));
}
private void prepare(Waybill waybill) {
if (isSentinelMinusOne(waybill.getQuantity())) waybill.setQuantity(null);
if (isSentinelMinusOne(waybill.getMileage())) waybill.setMileage(null);
@@ -0,0 +1,28 @@
-- MySQL 5.7+ 兼容:保存应收应付费用生成时实际命中的计费规则。
-- 请在 transport 数据库执行。本脚本使用 information_schema 判断,可重复执行。
DELIMITER $$
DROP PROCEDURE IF EXISTS `upgrade_receivable_payable_cargo_fee_billing_rules_20260830`$$
CREATE PROCEDURE `upgrade_receivable_payable_cargo_fee_billing_rules_20260830`()
BEGIN
DECLARE db_name varchar(128);
SET db_name = DATABASE();
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = db_name
AND table_name = 'blade_receivable_payable_cargo_fee'
AND column_name = 'billing_rules_json'
) THEN
ALTER TABLE `blade_receivable_payable_cargo_fee`
ADD COLUMN `billing_rules_json` text COLLATE utf8mb4_general_ci DEFAULT NULL
COMMENT '命中计费规则JSON' AFTER `billing_type`;
END IF;
END$$
CALL `upgrade_receivable_payable_cargo_fee_billing_rules_20260830`()$$
DROP PROCEDURE `upgrade_receivable_payable_cargo_fee_billing_rules_20260830`$$
DELIMITER ;
@@ -64,13 +64,14 @@ CREATE TABLE IF NOT EXISTS `blade_receivable_payable_cargo_fee` (
`detail_id` bigint(20) NOT NULL COMMENT '应收应付明细ID',
`waybill_id` bigint(20) DEFAULT NULL COMMENT '运单ID',
`line_no` varchar(30) DEFAULT NULL COMMENT '行号',
`data_source` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '自动生成' COMMENT '来源:自动生成/手录入',
`data_source` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '自动生成' COMMENT '来源:自动生成/手录入',
`cargo_name` varchar(100) DEFAULT NULL COMMENT '货物名称',
`cargo_type` varchar(100) DEFAULT NULL COMMENT '货物类型',
`specification` varchar(255) DEFAULT NULL COMMENT '规格',
`model` varchar(255) DEFAULT NULL COMMENT '型号',
`billing_factor` varchar(100) DEFAULT NULL COMMENT '计费要素',
`billing_type` varchar(100) DEFAULT NULL COMMENT '计费类型',
`billing_rules_json` text COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '命中计费规则JSON',
`transport_quantity` decimal(18,6) DEFAULT NULL COMMENT '运输量',
`quantity_unit` varchar(50) DEFAULT NULL COMMENT '数量单位',
`price_unit` varchar(50) DEFAULT NULL COMMENT '运费计算单位',
@@ -3,8 +3,8 @@ CREATE TABLE IF NOT EXISTS `blade_settlement_adjustment` (
`id` bigint(20) NOT NULL, `tenant_id` varchar(12) NOT NULL DEFAULT '000000',
`create_user` bigint(20) DEFAULT NULL, `create_dept` bigint(20) DEFAULT NULL, `create_time` datetime DEFAULT NULL,
`update_user` bigint(20) DEFAULT NULL, `update_time` datetime DEFAULT NULL, `status` int(11) DEFAULT '1', `is_deleted` int(11) DEFAULT '0',
`adjustment_no` varchar(100) NOT NULL COMMENT '结算调整单号', `formal_settlement_id` bigint(20) NOT NULL COMMENT '关联正式结算单ID',
`formal_settlement_no` varchar(100) NOT NULL COMMENT '关联正式结算单号', `settlement_type` varchar(30) NOT NULL,
`adjustment_no` varchar(100) NOT NULL COMMENT '结算调整单号', `formal_settlement_id` bigint(20) DEFAULT NULL COMMENT '关联正式结算单ID',
`formal_settlement_no` varchar(100) DEFAULT NULL COMMENT '关联正式结算单号', `settlement_type` varchar(30) DEFAULT NULL,
`project_name` varchar(100) DEFAULT NULL, `dept_name` varchar(100) DEFAULT NULL, `customer_name` varchar(200) DEFAULT NULL,
`contract_no` varchar(100) DEFAULT NULL, `contract_name` varchar(100) DEFAULT NULL,
`adjustment_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '调整金额',
@@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS `blade_settlement_adjustment_detail` (
`id` bigint(20) NOT NULL, `tenant_id` varchar(12) NOT NULL DEFAULT '000000',
`create_user` bigint(20) DEFAULT NULL, `create_dept` bigint(20) DEFAULT NULL, `create_time` datetime DEFAULT NULL,
`update_user` bigint(20) DEFAULT NULL, `update_time` datetime DEFAULT NULL, `status` int(11) DEFAULT '1', `is_deleted` int(11) DEFAULT '0',
`adjustment_id` bigint(20) NOT NULL, `formal_settlement_detail_id` bigint(20) NOT NULL, `formal_settlement_detail_fee_id` bigint(20) NOT NULL,
`adjustment_id` bigint(20) NOT NULL, `formal_settlement_detail_id` bigint(20) DEFAULT NULL, `formal_settlement_detail_fee_id` bigint(20) DEFAULT NULL,
`fee_type` varchar(100) DEFAULT NULL, `fee_item` varchar(200) DEFAULT NULL, `original_amount_tax` decimal(18,2) DEFAULT NULL,
`adjustment_amount_tax` decimal(18,2) NOT NULL DEFAULT '0.00', `adjustment_amount_no_tax` decimal(18,2) DEFAULT NULL,
`remark` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_adjustment_detail_bill` (`adjustment_id`),
@@ -0,0 +1,5 @@
-- 结算调整单草稿允许暂不完善关联正式结算信息
ALTER TABLE `blade_settlement_adjustment`
MODIFY COLUMN `formal_settlement_id` bigint(20) DEFAULT NULL COMMENT '关联正式结算单ID',
MODIFY COLUMN `formal_settlement_no` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '关联正式结算单号',
MODIFY COLUMN `settlement_type` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL;
@@ -0,0 +1,5 @@
-- 结算调整单支持手工新增费用
ALTER TABLE `blade_settlement_adjustment_detail`
MODIFY COLUMN `formal_settlement_detail_id` bigint(20) DEFAULT NULL COMMENT '正式结算明细ID,手工费用为空',
MODIFY COLUMN `formal_settlement_detail_fee_id` bigint(20) DEFAULT NULL COMMENT '正式结算明细费用ID,手工费用为空';
+2
View File
@@ -262,6 +262,7 @@ CREATE TABLE `blade_waybill` (
`escort_name` varchar(100) DEFAULT NULL COMMENT '押运人',
`escort_phone` varchar(50) DEFAULT NULL COMMENT '押运人手机号',
`mileage` decimal(18,2) DEFAULT NULL COMMENT '里程',
`mileage_remark` varchar(200) DEFAULT NULL COMMENT '里程维护备注',
`estimated_start_time` date DEFAULT NULL COMMENT '预计发货日期',
`estimated_end_time` date DEFAULT NULL COMMENT '预计完成日期',
`unit_price` decimal(18,2) DEFAULT NULL COMMENT '单价',
@@ -415,6 +416,7 @@ INSERT IGNORE INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `pa
(2090000000000000611, 2090000000000000600, 'waybill_manage_import', '导入运单', 'waybill_manage_import', '', '', 11, 2, 0, 1, NULL, '', 0),
(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),
(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),
@@ -0,0 +1,8 @@
ALTER TABLE `blade_waybill`
ADD COLUMN `mileage_remark` varchar(200) DEFAULT NULL COMMENT '里程维护备注' AFTER `mileage`;
INSERT INTO `blade_menu`
(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `component`, `remark`, `is_deleted`)
VALUES
(2090000000000000614, 2090000000000000600, 'waybill_manage_mileage', '维护里程', 'waybill_manage_mileage', '', '', 14, 2, 0, 1, NULL, '', 0)
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `alias` = VALUES(`alias`), `sort` = VALUES(`sort`), `is_deleted` = 0;