From 81f49babd060882a2150051490c9787cd8e36417 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 28 Aug 2026 15:30:12 +0800 Subject: [PATCH] fix bug --- .../dto/PaymentApplicationSaveRequest.java | 1 + .../entity/PaymentApplicationSettlement.java | 23 ++++ .../pojo/vo/PaymentApplicationVO.java | 2 + .../PaymentApplicationSettlementMapper.java | 10 ++ .../service/IFormalSettlementService.java | 2 + .../impl/FormalSettlementServiceImpl.java | 118 +++++++++++++++++- .../impl/PaymentApplicationServiceImpl.java | 100 ++++++++++++++- .../impl/ReceiptClaimRecordServiceImpl.java | 10 +- .../service/impl/ReceiptFlowServiceImpl.java | 8 +- .../TransportReconciliationServiceImpl.java | 95 ++++++++++++-- .../wrapper/FormalSettlementWrapper.java | 3 +- .../blade_payment_application_20260821.sql | 11 ++ 12 files changed, 353 insertions(+), 30 deletions(-) create mode 100644 blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplicationSettlement.java create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationSettlementMapper.java diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSaveRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSaveRequest.java index b24df78..bb453e1 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSaveRequest.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSaveRequest.java @@ -33,6 +33,7 @@ public class PaymentApplicationSaveRequest implements Serializable { private Long id; private String paymentType; private Long settlementId; + private List settlementIds; private Long preSettlementId; private Long projectId; private String projectName; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplicationSettlement.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplicationSettlement.java new file mode 100644 index 0000000..0f4dd8a --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplicationSettlement.java @@ -0,0 +1,23 @@ +package org.springblade.transport.pojo.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.tenant.mp.TenantEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** 付款申请关联正式结算单。 */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("blade_payment_application_settlement") +public class PaymentApplicationSettlement extends TenantEntity { + @Serial private static final long serialVersionUID = 1L; + private Long paymentApplicationId; + private Long formalSettlementId; + private String formalSettlementNo; + private BigDecimal settlementAmount; + private BigDecimal appliedAmount; + private BigDecimal paidAmount; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java index cbacc29..6e6c183 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java @@ -25,6 +25,7 @@ import lombok.EqualsAndHashCode; import org.springblade.transport.pojo.entity.PaymentApplication; import org.springblade.transport.pojo.entity.PaymentApplicationInvoice; import org.springblade.transport.pojo.entity.PaymentApplicationRecord; +import org.springblade.transport.pojo.entity.PaymentApplicationSettlement; import java.io.Serial; import java.time.LocalDate; @@ -44,4 +45,5 @@ public class PaymentApplicationVO extends PaymentApplication { @TableField(exist = false) private String kingdeeStatusName; @TableField(exist = false) private List invoices; @TableField(exist = false) private List paymentRecords; + @TableField(exist = false) private List settlements; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationSettlementMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationSettlementMapper.java new file mode 100644 index 0000000..78e0285 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationSettlementMapper.java @@ -0,0 +1,10 @@ +package org.springblade.transport.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.springblade.transport.pojo.entity.PaymentApplicationSettlement; + +/** 付款申请关联正式结算单 Mapper。 */ +@Mapper +public interface PaymentApplicationSettlementMapper extends BaseMapper { +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java index 710291a..85016e0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java @@ -41,6 +41,8 @@ public interface IFormalSettlementService extends BaseService String syncKingdee(Long id); List detailFees(Long detailId); void adjustDetail(PreSettlementDetailAdjustRequest request); + void refreshPaymentSummary(Long settlementId); + void refreshPaymentSummariesForPreSettlement(Long preSettlementId); String applyPayment(FormalSettlementPaymentRequest request); List applyPayments(FormalSettlementBatchPaymentRequest request); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index f873660..64a8b88 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -30,6 +30,8 @@ import org.springblade.transport.mapper.PreSettlementDetailFeeMapper; import org.springblade.transport.mapper.PreSettlementMapper; import org.springblade.transport.mapper.PreSettlementSummaryFeeMapper; import org.springblade.transport.mapper.PaymentApplicationMapper; +import org.springblade.transport.mapper.PaymentApplicationSettlementMapper; +import org.springblade.transport.mapper.ReceiptClaimSettlementMapper; import org.springblade.transport.mapper.ReceivablePayableDetailMapper; import org.springblade.transport.mapper.ReceivablePayableCargoFeeMapper; import org.springblade.transport.mapper.SettlementAdjustmentMapper; @@ -52,6 +54,8 @@ import org.springblade.transport.pojo.entity.PreSettlementDetail; import org.springblade.transport.pojo.entity.PreSettlementDetailFee; import org.springblade.transport.pojo.entity.PreSettlementSummaryFee; import org.springblade.transport.pojo.entity.PaymentApplication; +import org.springblade.transport.pojo.entity.PaymentApplicationSettlement; +import org.springblade.transport.pojo.entity.ReceiptClaimSettlement; import org.springblade.transport.pojo.entity.ReceivablePayableDetail; import org.springblade.transport.pojo.entity.ReceivablePayableCargoFee; import org.springblade.transport.pojo.entity.SettlementAdjustment; @@ -114,6 +118,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpllambdaQuery() + .eq(FormalSettlementSource::getPreSettlementId, preSettlementId) + .eq(FormalSettlementSource::getIsDeleted, 0)).stream() + .map(FormalSettlementSource::getFormalSettlementId).distinct() + .forEach(this::refreshPaymentSummary); } private List listSummaryFees(Long settlementId) { @@ -838,10 +869,91 @@ public class FormalSettlementServiceImpl extends BaseServiceImpllambdaQuery().eq(FormalSettlementSource::getFormalSettlementId, entity.getId())).stream().map(FormalSettlementSource::getPreSettlementNo).collect(Collectors.joining(","))); return vo; } + private PaymentSummary calculatePaymentSummary(FormalSettlement settlement) { + List preSettlementIds = sourceMapper.selectList(Wrappers.lambdaQuery() + .eq(FormalSettlementSource::getFormalSettlementId, settlement.getId()) + .eq(FormalSettlementSource::getIsDeleted, 0)).stream() + .map(FormalSettlementSource::getPreSettlementId).filter(Objects::nonNull).toList(); + LambdaQueryWrapper query = Wrappers.lambdaQuery() + .eq(PaymentApplication::getIsDeleted, 0); + if (preSettlementIds.isEmpty()) { + query.eq(PaymentApplication::getSettlementId, settlement.getId()); + } else { + query.and(wrapper -> wrapper.eq(PaymentApplication::getSettlementId, settlement.getId()) + .or().in(PaymentApplication::getPreSettlementId, preSettlementIds)); + } + List relations = paymentApplicationSettlementMapper.selectList( + Wrappers.lambdaQuery() + .eq(PaymentApplicationSettlement::getFormalSettlementId, settlement.getId()) + .eq(PaymentApplicationSettlement::getIsDeleted, 0)); + Set relationApplicationIds = relations.stream().map(PaymentApplicationSettlement::getPaymentApplicationId) + .filter(Objects::nonNull).collect(Collectors.toSet()); + if (!relationApplicationIds.isEmpty()) query.notIn(PaymentApplication::getId, relationApplicationIds); + List applications = paymentApplicationMapper.selectList(query); + BigDecimal appliedAmount = applications.stream() + .filter(item -> REVIEWING.equals(item.getApprovalStatus())) + .map(PaymentApplication::getAppliedAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal paidAmount = applications.stream() + .filter(item -> APPROVED.equals(item.getApprovalStatus())) + .map(PaymentApplication::getPaidAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add); + Map relationStatuses = relationApplicationIds.isEmpty() ? Map.of() : paymentApplicationMapper.selectList( + Wrappers.lambdaQuery().in(PaymentApplication::getId, relationApplicationIds)) + .stream().collect(Collectors.toMap(PaymentApplication::getId, PaymentApplication::getApprovalStatus)); + appliedAmount = appliedAmount.add(relations.stream() + .filter(item -> REVIEWING.equals(relationStatuses.get(item.getPaymentApplicationId()))) + .map(PaymentApplicationSettlement::getAppliedAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + paidAmount = paidAmount.add(relations.stream() + .filter(item -> APPROVED.equals(relationStatuses.get(item.getPaymentApplicationId()))) + .map(PaymentApplicationSettlement::getPaidAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + List legacyPayments = paymentMapper.selectList( + Wrappers.lambdaQuery() + .eq(FormalSettlementPayment::getFormalSettlementId, settlement.getId()) + .eq(FormalSettlementPayment::getIsDeleted, 0)); + appliedAmount = appliedAmount.add(legacyPayments.stream() + .filter(item -> REVIEWING.equals(item.getBillStatus())) + .map(FormalSettlementPayment::getAppliedAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + paidAmount = paidAmount.add(legacyPayments.stream() + .filter(item -> APPROVED.equals(item.getBillStatus())) + .map(FormalSettlementPayment::getPaidAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + paidAmount = paidAmount.add(sourceMapper.selectList(Wrappers.lambdaQuery() + .eq(FormalSettlementSource::getFormalSettlementId, settlement.getId()) + .eq(FormalSettlementSource::getIsDeleted, 0)).stream() + .map(FormalSettlementSource::getAdvancePaidAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + if ("receivable".equals(settlement.getSettlementType())) { + paidAmount = paidAmount.add(receiptClaimSettlementMapper.selectList( + Wrappers.lambdaQuery() + .eq(ReceiptClaimSettlement::getFormalSettlementId, settlement.getId()) + .eq(ReceiptClaimSettlement::getStatus, 1)) + .stream().map(ReceiptClaimSettlement::getAllocatedReceiptAmount).map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + } + return new PaymentSummary(money(appliedAmount), money(paidAmount)); + } + + private String paymentStatus(BigDecimal paidAmount, BigDecimal settlementAmount) { + if (paidAmount.compareTo(BigDecimal.ZERO) <= 0) return "unpaid"; + return paidAmount.compareTo(money(settlementAmount)) >= 0 ? "paid" : "partial"; + } + + private record PaymentSummary(BigDecimal appliedAmount, BigDecimal paidAmount) { + } + private SettlementAdjustmentVO toAdjustmentVO(SettlementAdjustment entity, String kingdeeBillNo) { SettlementAdjustmentVO vo = Objects.requireNonNull( BeanUtil.copyProperties(entity, SettlementAdjustmentVO.class)); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java index 842e01a..f8195ac 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java @@ -39,6 +39,7 @@ import org.springblade.transport.mapper.ContractManageMapper; import org.springblade.transport.mapper.PaymentApplicationInvoiceMapper; import org.springblade.transport.mapper.PaymentApplicationMapper; import org.springblade.transport.mapper.PaymentApplicationRecordMapper; +import org.springblade.transport.mapper.PaymentApplicationSettlementMapper; import org.springblade.transport.mapper.TemporaryCreditLimitMapper; import org.springblade.transport.pojo.dto.PaymentApplicationInvoiceRequest; import org.springblade.transport.pojo.dto.PaymentApplicationRecordRequest; @@ -54,10 +55,12 @@ import org.springblade.transport.pojo.entity.ContractManage; import org.springblade.transport.pojo.entity.PaymentApplication; import org.springblade.transport.pojo.entity.PaymentApplicationInvoice; import org.springblade.transport.pojo.entity.PaymentApplicationRecord; +import org.springblade.transport.pojo.entity.PaymentApplicationSettlement; import org.springblade.transport.pojo.entity.TemporaryCreditLimit; import org.springblade.transport.pojo.vo.PaymentApplicationVO; import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO; import org.springblade.transport.service.IPaymentApplicationService; +import org.springblade.transport.service.IFormalSettlementService; import org.springblade.transport.wrapper.PaymentApplicationWrapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -84,6 +87,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl selectPage(IPage page, PaymentApplicationVO query) { @@ -122,6 +127,10 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpllambdaQuery() .eq(org.springblade.transport.pojo.entity.PaymentApplicationRecord::getPaymentApplicationId, id) .orderByDesc(org.springblade.transport.pojo.entity.PaymentApplicationRecord::getCreateTime))); + vo.setSettlements(settlementRelationMapper.selectList(Wrappers.lambdaQuery() + .eq(PaymentApplicationSettlement::getPaymentApplicationId, id) + .eq(PaymentApplicationSettlement::getIsDeleted, 0) + .orderByAsc(PaymentApplicationSettlement::getCreateTime))); return vo; } @@ -191,7 +200,13 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl formalSettlements = resolveFormalSettlements(request, entity); + if (formalSettlements.size() > 1) fillFormalAggregateReference(entity, formalSettlements); fillBillLedger(entity, request); validateQuota(entity); saveOrUpdate(entity); @@ -225,11 +240,12 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl 0 ? "matched" : "unmatched"); entity.setPaidAmount(paidAmount); updateById(entity); + saveSettlementRelations(entity, formalSettlements); return entity.getId(); } @Override @Transactional(rollbackFor = Exception.class) - public void removeDraft(Long id) { PaymentApplication entity = editable(id); invoiceMapper.delete(Wrappers.lambdaQuery().eq(PaymentApplicationInvoice::getPaymentApplicationId, id)); recordMapper.delete(Wrappers.lambdaQuery().eq(PaymentApplicationRecord::getPaymentApplicationId, id)); removeById(entity); } + public void removeDraft(Long id) { PaymentApplication entity = editable(id); invoiceMapper.delete(Wrappers.lambdaQuery().eq(PaymentApplicationInvoice::getPaymentApplicationId, id)); recordMapper.delete(Wrappers.lambdaQuery().eq(PaymentApplicationRecord::getPaymentApplicationId, id)); settlementRelationMapper.delete(Wrappers.lambdaQuery().eq(PaymentApplicationSettlement::getPaymentApplicationId, id)); removeById(entity); } @Override @Transactional(rollbackFor = Exception.class) public void submit(PaymentApplicationStatusRequest request) { @@ -244,6 +260,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl 0)) throw new ServiceException("付款比例必须在0-100之间"); if (request.getAppliedAmount() == null || request.getAppliedAmount().compareTo(BigDecimal.ZERO) < 0) throw new ServiceException("申请付款金额不能小于0"); - if (!"project_advance".equals(request.getPaymentType()) && request.getSettlementId() == null && request.getPreSettlementId() == null) throw new ServiceException("非项目预付必须关联结算单"); + if (!"project_advance".equals(request.getPaymentType()) && request.getSettlementId() == null + && request.getPreSettlementId() == null && Func.isEmpty(request.getSettlementIds())) throw new ServiceException("非项目预付必须关联结算单"); if ("project_advance".equals(request.getPaymentType()) && request.getProjectId() == null) throw new ServiceException("项目预付必须选择所属项目"); if ("progress_advance".equals(request.getPaymentType()) && request.getPreSettlementId() == null) throw new ServiceException("进度预付必须关联预结算单"); - if ("settlement_payment".equals(request.getPaymentType()) && request.getSettlementId() == null) throw new ServiceException("结算付款必须关联正式结算单"); + if ("settlement_payment".equals(request.getPaymentType()) && request.getSettlementId() == null + && Func.isEmpty(request.getSettlementIds())) throw new ServiceException("结算付款必须关联正式结算单"); } private void validateInvoice(PaymentApplicationInvoiceRequest invoice) { @@ -383,7 +404,70 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl 0 && money(entity.getPayableAmount()).compareTo(BigDecimal.ZERO) > 0) throw new ServiceException("申请付款金额不能超过可付款金额"); + if (!hasMultipleFormalReferences(request) + && money(entity.getAppliedAmount()).compareTo(money(entity.getPayableAmount())) > 0 + && money(entity.getPayableAmount()).compareTo(BigDecimal.ZERO) > 0) throw new ServiceException("申请付款金额不能超过可付款金额"); + } + + private List resolveFormalSettlements(PaymentApplicationSaveRequest request, PaymentApplication entity) { + if (!"settlement_payment".equals(request.getPaymentType())) return List.of(); + List ids = request.getSettlementIds() == null ? List.of() : request.getSettlementIds(); + if (ids.isEmpty() && request.getSettlementId() != null) ids = List.of(request.getSettlementId()); + ids = ids.stream().filter(Objects::nonNull).distinct().toList(); + List settlements = ids.stream().map(id -> formalSettlementMapper.selectById(id)).toList(); + if (settlements.stream().anyMatch(item -> item == null || Objects.equals(item.getIsDeleted(), 1) + || !APPROVED.equals(item.getApprovalStatus()) || !"payable".equals(item.getSettlementType()))) { + throw new ServiceException("只能选择审批通过的应付正式结算单"); + } + Long contractId = settlements.get(0).getContractId(); + if (contractId == null || settlements.stream().anyMatch(item -> !Objects.equals(contractId, item.getContractId()))) { + throw new ServiceException("多选正式结算单必须属于同一合同"); + } + return settlements; + } + + private boolean hasMultipleFormalReferences(PaymentApplicationSaveRequest request) { + return "settlement_payment".equals(request.getPaymentType()) && request.getSettlementIds() != null + && request.getSettlementIds().stream().filter(Objects::nonNull).distinct().count() > 1; + } + + private void fillFormalAggregateReference(PaymentApplication entity, List settlements) { + BigDecimal settlementAmount = settlements.stream().map(FormalSettlement::getSettlementAmount) + .map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal payableAmount = settlements.stream() + .map(item -> money(item.getSettlementAmount()).subtract(cumulativeAppliedAmount("settlement_payment", item.getId(), entity.getId())).max(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (money(entity.getAppliedAmount()).compareTo(payableAmount) > 0) throw new ServiceException("申请付款金额不能超过所选结算单可付款金额合计"); + FormalSettlement first = settlements.get(0); + entity.setSettlementId(first.getId()); + entity.setSettlementNo(settlements.stream().map(FormalSettlement::getFormalSettlementNo).filter(Objects::nonNull).collect(java.util.stream.Collectors.joining("、"))); + entity.setSettlementAmount(settlementAmount); + entity.setPayableAmount(payableAmount); + } + + private void saveSettlementRelations(PaymentApplication entity, List settlements) { + if (settlements.isEmpty()) return; + settlementRelationMapper.delete(Wrappers.lambdaQuery() + .eq(PaymentApplicationSettlement::getPaymentApplicationId, entity.getId())); + BigDecimal totalBase = settlements.stream().map(item -> money(item.getSettlementAmount()).subtract( + cumulativeAppliedAmount("settlement_payment", item.getId(), entity.getId())).max(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal remainingApplied = money(entity.getAppliedAmount()); + BigDecimal remainingPaid = money(entity.getPaidAmount()); + for (int index = 0; index < settlements.size(); index++) { + FormalSettlement settlement = settlements.get(index); + BigDecimal base = money(settlement.getSettlementAmount()).subtract( + cumulativeAppliedAmount("settlement_payment", settlement.getId(), entity.getId())).max(BigDecimal.ZERO); + BigDecimal applied = index == settlements.size() - 1 ? remainingApplied + : money(entity.getAppliedAmount()).multiply(base).divide(totalBase, 2, RoundingMode.DOWN); + BigDecimal paid = index == settlements.size() - 1 ? remainingPaid + : money(entity.getPaidAmount()).multiply(applied).divide(money(entity.getAppliedAmount()).max(BigDecimal.ONE), 2, RoundingMode.DOWN); + PaymentApplicationSettlement relation = new PaymentApplicationSettlement(); + relation.setPaymentApplicationId(entity.getId()); relation.setFormalSettlementId(settlement.getId()); + relation.setFormalSettlementNo(settlement.getFormalSettlementNo()); relation.setSettlementAmount(money(settlement.getSettlementAmount())); + relation.setAppliedAmount(applied); relation.setPaidAmount(paid); settlementRelationMapper.insert(relation); + remainingApplied = remainingApplied.subtract(applied); remainingPaid = remainingPaid.subtract(paid); + } } private void validateQuota(PaymentApplication entity) { @@ -546,7 +630,13 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpllambdaQuery().likeRight(PaymentApplication::getPaymentNo, prefix)) + 1); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceiptClaimRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceiptClaimRecordServiceImpl.java index 50b550d..bcf456a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceiptClaimRecordServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceiptClaimRecordServiceImpl.java @@ -45,6 +45,7 @@ import org.springblade.transport.pojo.entity.ReceiptClaimSettlement; import org.springblade.transport.pojo.entity.ReceiptFlowRecord; import org.springblade.transport.pojo.vo.ReceiptClaimRecordVO; import org.springblade.transport.service.IReceiptClaimRecordService; +import org.springblade.transport.service.IFormalSettlementService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -71,6 +72,7 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl selectPage(IPage page, ReceiptFlowVO query) { @@ -212,11 +214,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl internals = internalRows(id); + List externals = externalRows(id); + int internalUnmatched = (int) internals.stream().filter(item -> !MATCHED.equals(item.getMatchResult())).count(); + int externalUnmatched = (int) externals.stream().filter(item -> !MATCHED.equals(item.getMatchStatus())).count(); + int differenceCount = Math.abs(internals.size() - externals.size()) + Math.min(internalUnmatched, externalUnmatched); + BigDecimal differenceQuantity = sumInternalQuantity(internals).subtract(sumExternalQuantity(externals)).abs(); + BigDecimal differenceAmount = sumInternalAmount(internals).subtract(sumExternalAmount(externals)).abs(); + if (differenceCount != 0 || differenceQuantity.compareTo(BigDecimal.ZERO) != 0 + || differenceAmount.compareTo(BigDecimal.ZERO) != 0) { throw new ServiceException("差异单数、差异货量和差异金额必须全部为0才可完成对账"); } + assertAllMatched(bill); bill.setReconciliationStatus(COMPLETED); bill.setCompletedTime(LocalDateTime.now()); updateById(bill); @@ -370,6 +386,7 @@ public class TransportReconciliationServiceImpl BeanUtil.copyProperties(detail, row); row.setId(null); row.setReconciliationId(billId); row.setFormalSettlementDetailId(detail.getId()); row.setSourceDetailId(detail.getSourceDetailId()); row.setLineNo(lineNo); row.setMatchResult(UNMATCHED); row.setUpdateResult("not_updated"); + fillInternalAddresses(row, detail); if (fee != null) { row.setFormalSettlementDetailFeeId(fee.getId()); row.setSourceCargoFeeId(fee.getSourceFeeId()); row.setCargoName(fee.getCargoName()); row.setCargoType(fee.getCargoType()); row.setTransportQuantity(fee.getTransportQuantity()); @@ -383,6 +400,47 @@ public class TransportReconciliationServiceImpl internalMapper.insert(row); } + private void fillInternalAddresses(TransportReconciliationInternal row, FormalSettlementDetail detail) { + ReceivablePayableDetail source = detail.getSourceDetailId() == null ? null + : receivablePayableMapper.selectById(detail.getSourceDetailId()); + if (source != null && MASTER_ORDER_SOURCE.equals(source.getSourceType())) { + MasterOrder masterOrder = masterOrderMapper.selectOne(Wrappers.lambdaQuery() + .eq(MasterOrder::getMasterNo, source.getWaybillNo())); + if (masterOrder != null) { + copyAddresses(row, masterOrder.getDepartureAddress(), masterOrder.getDepartureName(), + masterOrder.getArrivalAddress(), masterOrder.getArrivalName()); + return; + } + } + if (source != null && LOADING_ORDER_SOURCE.equals(source.getSourceType())) { + LoadingManage loading = loadingManageMapper.selectOne(Wrappers.lambdaQuery() + .eq(LoadingManage::getLoadingNo, source.getWaybillNo())); + if (loading != null) { + copyAddresses(row, loading.getDepartureAddress(), null, loading.getArrivalAddress(), null); + return; + } + } + Long waybillId = detail.getWaybillId() != null ? detail.getWaybillId() + : source == null ? null : source.getWaybillId(); + Waybill waybill = waybillId == null ? null : waybillMapper.selectById(waybillId); + if (waybill == null) { + String waybillNo = firstNotEmpty(detail.getWaybillNo(), source == null ? null : source.getWaybillNo()); + if (Func.isNotEmpty(waybillNo)) { + waybill = waybillMapper.selectOne(Wrappers.lambdaQuery().eq(Waybill::getWaybillNo, waybillNo)); + } + } + if (waybill != null) { + copyAddresses(row, waybill.getDepartureAddress(), waybill.getDepartureName(), + waybill.getArrivalAddress(), waybill.getArrivalName()); + } + } + + private void copyAddresses(TransportReconciliationInternal row, String departureAddress, String departureName, + String arrivalAddress, String arrivalName) { + row.setDepartureAddress(firstNotEmpty(departureAddress, departureName, row.getDepartureAddress())); + row.setArrivalAddress(firstNotEmpty(arrivalAddress, arrivalName, row.getArrivalAddress())); + } + private void applyAmount(TransportReconciliation bill, TransportReconciliationInternal internal, TransportReconciliationExternal external) { BigDecimal before = money(internal.getSettlementAmount()); BigDecimal after = money(external.getSettlementAmount()); @@ -395,7 +453,18 @@ public class TransportReconciliationServiceImpl } } else { FormalSettlementDetail detail = formalDetailMapper.selectById(internal.getFormalSettlementDetailId()); - detail.setSettlementAmountTax(after); detail.setAdjustAmount(after.subtract(money(detail.getOriginalAmount()))); formalDetailMapper.updateById(detail); + List fees = formalDetailFeeMapper.selectList(Wrappers.lambdaQuery() + .eq(FormalSettlementDetailFee::getFormalSettlementDetailId, internal.getFormalSettlementDetailId())); + if (fees.size() == 1) { + FormalSettlementDetailFee fee = fees.get(0); + fee.setSettlementAmountTax(after); fee.setAdjustAmount(after.subtract(money(fee.getOriginalAmount()))); formalDetailFeeMapper.updateById(fee); + if (fee.getSourceFeeId() != null) { + ReceivablePayableCargoFee sourceFee = cargoFeeMapper.selectById(fee.getSourceFeeId()); + if (sourceFee != null) { sourceFee.setAfterAmount(after); sourceFee.setAdjustAmount(after.subtract(money(sourceFee.getOriginalAmount()))); cargoFeeMapper.updateById(sourceFee); } + } + } else { + detail.setSettlementAmountTax(after); detail.setAdjustAmount(after.subtract(money(detail.getOriginalAmount()))); formalDetailMapper.updateById(detail); + } ReceivablePayableDetail source = receivablePayableMapper.selectById(internal.getSourceDetailId()); if (source != null) { source.setTotalAmount(after); receivablePayableMapper.updateById(source); } } @@ -537,10 +606,14 @@ public class TransportReconciliationServiceImpl private LocalDateTime parseTimeNullable(String value, String field) { if (Func.isEmpty(value)) return null; - for (String pattern : List.of("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm")) { + for (String pattern : List.of("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-M-d HH:mm:ss", "yyyy-M-d HH:mm", + "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/M/d HH:mm:ss", "yyyy/M/d HH:mm")) { try { return LocalDateTime.parse(value.trim(), DateTimeFormatter.ofPattern(pattern)); } catch (DateTimeParseException ignored) { } } - throw new ServiceException(field + "格式应为yyyy-MM-dd HH:mm:ss"); + for (String pattern : List.of("yyyy-MM-dd", "yyyy-M-d", "yyyy/MM/dd", "yyyy/M/d")) { + try { return LocalDate.parse(value.trim(), DateTimeFormatter.ofPattern(pattern)).atStartOfDay(); } catch (DateTimeParseException ignored) { } + } + throw new ServiceException(field + "格式应为yyyy-MM-dd HH:mm:ss或yyyy-MM-dd"); } private String vehicleKey(TransportReconciliationInternal row) { return key(row.getVehicleNo(), row.getCargoName(), row.getActualDepartureTime(), row.getBatchNo(), row.getTransportQuantity()); } @@ -549,6 +622,10 @@ public class TransportReconciliationServiceImpl private String cargoKey(TransportReconciliationExternal row) { return key(row.getVehicleNo(), row.getDepartureAddress(), row.getArrivalAddress(), row.getCargoName(), row.getActualDepartureTime(), row.getTransportQuantity()); } private String key(Object... values) { StringBuilder builder = new StringBuilder(); for (Object value : values) builder.append(normal(value)).append('|'); return builder.toString(); } private String normal(Object value) { if (value == null) return ""; if (value instanceof BigDecimal decimal) return decimal.stripTrailingZeros().toPlainString(); return value.toString().trim().replaceAll("\\s+", "").toLowerCase(); } + private String firstNotEmpty(String... values) { + for (String value : values) if (Func.isNotEmpty(value)) return value; + return null; + } private boolean equalMoney(BigDecimal left, BigDecimal right) { return money(left).compareTo(money(right)) == 0; } private BigDecimal money(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private BigDecimal nonNegative(BigDecimal value, String field) { if (value == null || value.compareTo(BigDecimal.ZERO) < 0) throw new ServiceException(field + "不能小于0"); return value; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/FormalSettlementWrapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/FormalSettlementWrapper.java index 4f4a331..bb14a73 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/FormalSettlementWrapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/FormalSettlementWrapper.java @@ -32,8 +32,9 @@ public class FormalSettlementWrapper extends BaseEntityWrapper