feat: 正式结算单支持结算调整重推克隆与金蝶同步回填

This commit is contained in:
刘泉佳
2026-10-16 19:46:13 +08:00
parent bf9c1cc890
commit 16dfae2f7e
3 changed files with 137 additions and 3 deletions
@@ -24,6 +24,8 @@ import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest;
import java.util.Map;
/**
* 正式结算单服务
*
@@ -93,4 +95,29 @@ public interface IFormalSettlementService extends BaseService<FormalSettlement>
String applyPayment(FormalSettlementPaymentRequest request);
List<String> applyPayments(FormalSettlementBatchPaymentRequest request);
void claimInvoices(FormalSettlementInvoiceClaimRequest request);
/**
* 结算调整重推:克隆原正式结算单为新单(含明细、明细费项、汇总费项、来源预结算、附件、汇率),
* 新单置审批通过、未同步金蝶,来源原单号指向原单;下游关联(开票申请/收款认领/对账)不迁移。
* 不推送金蝶,由调用方在事务外调用 syncKingdee 推送
*
* @param sourceSettlementId 原正式结算单id
* @return 新单id与「原明细/原费项 -&gt; 新明细/新费项」id映射(供调整明细应用到新单)
*/
CloneResult cloneFormalSettlement(Long sourceSettlementId);
/**
* 结算调整重推收尾:原单标记「已调整」、旧锁解锁、追加重推变更记录(不改原单业务数据)
*
* @param settlementId 原正式结算单id
* @param adjustmentNo 结算调整单号
* @param newSettlementNo 重推生成的新单号
*/
void markSettlementAdjusted(Long settlementId, String adjustmentNo, String newSettlementNo);
/**
* 克隆结果:新单id + 原明细id-&gt;新明细id + 原费项id-&gt;新费项id
*/
record CloneResult(Long settlementId, Map<Long, Long> detailIdMap, Map<Long, Long> feeIdMap) {
}
}
@@ -344,7 +344,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
vo.setAdjustments(settlementAdjustmentMapper.selectList(Wrappers.<SettlementAdjustment>lambdaQuery()
.eq(SettlementAdjustment::getFormalSettlementId, id)
.orderByDesc(SettlementAdjustment::getCreateTime)).stream()
.map(item -> toAdjustmentVO(item, settlement.getKingdeeBillNo())).toList());
.map(this::toAdjustmentVO).toList());
vo.setChangeRecords(changeRecordMapper.selectList(Wrappers.<FormalSettlementChangeRecord>lambdaQuery()
.eq(FormalSettlementChangeRecord::getFormalSettlementId, id)
.orderByDesc(FormalSettlementChangeRecord::getChangeTime)));
@@ -636,6 +636,102 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
changeRecordMapper.insert(record);
}
@Override
@Transactional(rollbackFor = Exception.class)
public CloneResult cloneFormalSettlement(Long sourceSettlementId) {
FormalSettlement source = existing(sourceSettlementId);
FormalSettlement clone = Objects.requireNonNull(BeanUtil.copyProperties(source, FormalSettlement.class));
// 新单头:新号、直接审批通过、未同步金蝶、来源原单号;不动原单
resetAuditFields(clone);
clone.setFormalSettlementNo(nextNo(source.getSettlementType()));
clone.setApprovalStatus(TransportBusiConstant.APPROVED);
clone.setCurrentNode("审批通过");
clone.setCurrentProcessor(AuthUtil.getUserName());
clone.setApprovedTime(LocalDateTime.now());
clone.setKingdeeBillNo(null);
clone.setKingdeeBillId(null);
clone.setKingdeeSyncStatus(KingdeeBfiConstant.KINGDEE_STATUS_UNSYNCED);
clone.setSyncedTime(null);
clone.setVoidReason(null);
clone.setOriginFormalSettlementNo(source.getFormalSettlementNo());
clone.setAdjustStatus(null);
// 下游关联(开票申请/收款认领/对账)不迁移:发票/收付款口径归零,新单为全新独立单
clone.setInvoiceStatus("unreceived");
clone.setPaymentStatus("unpaid");
clone.setInvoiceAmount(BigDecimal.ZERO.setScale(2));
clone.setAppliedPaymentAmount(BigDecimal.ZERO.setScale(2));
clone.setPaidAmount(BigDecimal.ZERO.setScale(2));
clone.setRemainingPayableAmount(money(source.getSettlementAmount()));
save(clone);
// 明细与明细费项克隆,记录 原id->新id 映射(调整明细应用到新单时按映射定位)
Map<Long, Long> detailIdMap = new HashMap<>();
Map<Long, Long> feeIdMap = new HashMap<>();
List<FormalSettlementDetail> details = detailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
.eq(FormalSettlementDetail::getFormalSettlementId, sourceSettlementId)
.eq(FormalSettlementDetail::getIsDeleted, 0)
.orderByAsc(FormalSettlementDetail::getLineNo));
for (FormalSettlementDetail detail : details) {
FormalSettlementDetail cloneDetail = Objects.requireNonNull(BeanUtil.copyProperties(detail, FormalSettlementDetail.class));
resetAuditFields(cloneDetail);
cloneDetail.setFormalSettlementId(clone.getId());
detailMapper.insert(cloneDetail);
detailIdMap.put(detail.getId(), cloneDetail.getId());
for (FormalSettlementDetailFee fee : detailFeeMapper.selectList(Wrappers.<FormalSettlementDetailFee>lambdaQuery()
.eq(FormalSettlementDetailFee::getFormalSettlementDetailId, detail.getId())
.orderByAsc(FormalSettlementDetailFee::getLineNo))) {
FormalSettlementDetailFee cloneFee = Objects.requireNonNull(BeanUtil.copyProperties(fee, FormalSettlementDetailFee.class));
resetAuditFields(cloneFee);
cloneFee.setFormalSettlementDetailId(cloneDetail.getId());
detailFeeMapper.insert(cloneFee);
feeIdMap.put(fee.getId(), cloneFee.getId());
}
}
// 汇总费用项克隆
for (FormalSettlementSummaryFee summaryFee : listSummaryFees(sourceSettlementId)) {
FormalSettlementSummaryFee cloneFee = Objects.requireNonNull(BeanUtil.copyProperties(summaryFee, FormalSettlementSummaryFee.class));
resetAuditFields(cloneFee);
cloneFee.setFormalSettlementId(clone.getId());
summaryFeeMapper.insert(cloneFee);
}
// 来源预结算重建(接续原单来源,不重复锁定 PreSettlement.formalSettlementNo)
for (FormalSettlementSource relation : sourceMapper.selectList(Wrappers.<FormalSettlementSource>lambdaQuery()
.eq(FormalSettlementSource::getFormalSettlementId, sourceSettlementId))) {
FormalSettlementSource cloneRelation = Objects.requireNonNull(BeanUtil.copyProperties(relation, FormalSettlementSource.class));
resetAuditFields(cloneRelation);
cloneRelation.setFormalSettlementId(clone.getId());
sourceMapper.insert(cloneRelation);
}
log.info("结算调整重推克隆正式结算单:原单[{}] -> 新单[{}](明细{}条)",
source.getFormalSettlementNo(), clone.getFormalSettlementNo(), details.size());
return new CloneResult(clone.getId(), detailIdMap, feeIdMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void markSettlementAdjusted(Long settlementId, String adjustmentNo, String newSettlementNo) {
existing(settlementId);
// 原单只加标记,业务字段不动
update(Wrappers.<FormalSettlement>lambdaUpdate()
.eq(FormalSettlement::getId, settlementId)
.set(FormalSettlement::getAdjustStatus, "adjusted"));
kingdeeBillLockService.unlockByAdjustRepush(KingdeeLockConstant.SOURCE_SETTLEMENT, settlementId, AuthUtil.getUserName());
saveChange(settlementId, "结算单基本信息", null, "调整",
"结算调整单" + Func.toStr(adjustmentNo) + "重推生成新单" + Func.toStr(newSettlementNo) + ",原单标记已调整", null);
}
/** 重推/克隆复用:清空审计与主键字段,交由 MP 自动填充 */
private void resetAuditFields(org.springblade.core.mp.base.BaseEntity entity) {
entity.setId(null);
entity.setCreateUser(null);
entity.setCreateDept(null);
entity.setCreateTime(null);
entity.setUpdateUser(null);
entity.setUpdateTime(null);
entity.setStatus(null);
entity.setIsDeleted(null);
}
@Override
public String syncKingdee(Long id) {
String billNo;
@@ -700,6 +796,11 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
.ne(FormalSettlement::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED)
.set(FormalSettlement::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_FAILED));
if (updated > 0) {
// 结算调整单回填:未推送场景审批通过后自动推送失败,调整单置 failed 供「推送金蝶」重试
settlementAdjustmentMapper.update(null, Wrappers.<SettlementAdjustment>lambdaUpdate()
.eq(SettlementAdjustment::getFormalSettlementId, id)
.eq(SettlementAdjustment::getApprovalStatus, TransportBusiConstant.APPROVED)
.set(SettlementAdjustment::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_FAILED));
// 失败原因写变更记录留档(推送事务已回滚,此处独立事务);原因过长截断留档,不抛错
String failReason = Func.toStrWithEmpty(reason, "未知异常");
saveChange(id, "结算单基本信息", null, "调整",
@@ -851,6 +952,12 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
settlement.setSyncedTime(LocalDateTime.now());
updateById(settlement);
saveChange(settlement.getId(), "结算单基本信息", null, "调整", changeDesc, "");
// 结算调整单回填:未推送场景审批通过后自动推送成功,回填挂在本单的已审批调整单(重推场景调整单已改挂新单,不受影响)
settlementAdjustmentMapper.update(null, Wrappers.<SettlementAdjustment>lambdaUpdate()
.eq(SettlementAdjustment::getFormalSettlementId, settlement.getId())
.eq(SettlementAdjustment::getApprovalStatus, TransportBusiConstant.APPROVED)
.set(SettlementAdjustment::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED)
.set(SettlementAdjustment::getKingdeeBillNo, billNo));
log.info("正式结算单[{}]同步金蝶完成:settlementType={},billNo={},billPkId={}",
settlement.getFormalSettlementNo(), settlement.getSettlementType(), billNo, billPkId);
return billNo;
@@ -1821,13 +1928,12 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
List<FormalSettlementSource> sources, BigDecimal receiptClaimPaid) {
}
private SettlementAdjustmentVO toAdjustmentVO(SettlementAdjustment entity, String kingdeeBillNo) {
private SettlementAdjustmentVO toAdjustmentVO(SettlementAdjustment entity) {
SettlementAdjustmentVO vo = Objects.requireNonNull(
BeanUtil.copyProperties(entity, SettlementAdjustmentVO.class));
vo.setCreateUserName(UserCache.getUserRealName(entity.getCreateUser()));
vo.setApprovalStatusName(approvalStatusName(entity.getApprovalStatus()));
vo.setSettlementTypeName("receivable".equals(entity.getSettlementType()) ? "应收" : "应付");
vo.setKingdeeBillNo(kingdeeBillNo);
return vo;
}
@@ -45,6 +45,7 @@ public class FormalSettlementWrapper extends BaseEntityWrapper<FormalSettlement,
case "voided" -> "已作废";
default -> entity.getApprovalStatus();
});
vo.setAdjustStatusName("adjusted".equals(entity.getAdjustStatus()) ? "已调整" : null);
return vo;
}
}