feat: 结算调整单审批自动推送与冲销后重推新单

This commit is contained in:
刘泉佳
2026-10-16 19:46:27 +08:00
parent 16dfae2f7e
commit 784c532d9c
6 changed files with 261 additions and 20 deletions
@@ -17,7 +17,40 @@ public interface ISettlementAdjustmentService {
Long saveDraft(SettlementAdjustmentSaveRequest request);
void removeDraft(Long id);
void submit(SettlementAdjustmentStatusRequest request);
void approve(SettlementAdjustmentStatusRequest request);
/**
* 审批通过:未推送场景回写原单并发布金蝶自动推送事件;已推送场景原单零改动仅追加变更记录。
*
* @param request 审批请求
* @return 已推送场景的手工冲销提示文案,未推送场景返回 null
*/
String approve(SettlementAdjustmentStatusRequest request);
void returnBill(SettlementAdjustmentStatusRequest request);
String repush(Long adjustmentId);
/**
* 选单后金蝶冲销预检(软提示,不阻断建单):未推送 / 已推送已冲销 / 已推送未冲销;
* 金蝶查询异常降级返回「未校验」,由审批分流与重推闸门兜底
*
* @param formalSettlementId 选定正式结算单id
* @return syncStatus / writtenOff / message
*/
Map<String, Object> kingdeeWriteOffPrecheck(Long formalSettlementId);
/**
* 推送金蝶(未推送/生成失败场景):推送关联正式结算单,成功后由正式结算单回填调整单金蝶单号与同步状态
*
* @param adjustmentId 结算调整单id
* @return 金蝶单据编号
*/
String syncKingdee(Long adjustmentId);
/**
* 重推克隆+应用(独立事务,由 repush 经自代理调用):克隆原单为新正式结算单并应用调整,
* 供推送失败重试时幂等复用
*
* @param originalSettlementId 原正式结算单id
* @param adjustmentId 结算调整单id
* @return 新正式结算单id
*/
Long cloneAndApplyForRepush(Long originalSettlementId, Long adjustmentId);
}
@@ -32,6 +32,9 @@ public final class KingdeeLockConstant {
/** 来源操作类型:单据删除(解锁回调携带 DELETE 时 WLTMS 废除源单并还原关联数据) */
public static final String OPERATION_DELETE = "DELETE";
/** 来源操作类型:结算调整重推(结算调整单重推后解锁原结算单旧锁记录,非金蝶回调) */
public static final String OPERATION_ADJUST_REPUSH = "ADJUST_REPUSH";
/** 系统标识:WLTMS(运输管理系统,即本系统) */
public static final String SYSTEM_WLTMS = "WLTMS";
@@ -7,6 +7,8 @@ import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.transport.listener.KingdeeApprovalSyncListener;
import org.springblade.transport.pojo.dto.SettlementAdjustmentSaveRequest;
import org.springblade.transport.pojo.dto.SettlementAdjustmentStatusRequest;
import org.springblade.transport.pojo.entity.SettlementAdjustment;
@@ -33,7 +35,20 @@ public class SettlementAdjustmentController extends BladeController {
@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("提交成功"); }
@PostMapping("/approve") public R approve(@RequestBody SettlementAdjustmentStatusRequest request) { service.approve(request); return R.success("审批通过"); }
@PostMapping("/approve") public R approve(@RequestBody SettlementAdjustmentStatusRequest request) {
String hint = service.approve(request);
// 未推送场景自动推送失败原因以警告文案返回(data=false 供前端区分提示样式)
String syncFailure = KingdeeApprovalSyncListener.pollSyncFailure();
if (Func.isNotBlank(syncFailure)) {
return R.data(Boolean.FALSE, "审批通过,但自动同步金蝶失败:" + syncFailure);
}
if (Func.isNotBlank(hint)) {
return R.data(Boolean.TRUE, "审批通过;" + hint);
}
return R.success("审批通过");
}
@PostMapping("/return") public R returnBill(@RequestBody SettlementAdjustmentStatusRequest request) { service.returnBill(request); return R.success("已驳回"); }
@PostMapping("/repush") public R<String> repush(@RequestParam Long id) { return R.data(service.repush(id)); }
@GetMapping("/kingdee-writeoff-precheck") public R<Map<String, Object>> kingdeeWriteOffPrecheck(@RequestParam Long formalSettlementId) { return R.data(service.kingdeeWriteOffPrecheck(formalSettlementId)); }
@PostMapping("/sync-kingdee") public R<String> syncKingdee(@RequestParam Long id) { return R.data(service.syncKingdee(id)); }
}
@@ -60,6 +60,17 @@ public interface IKingdeeBillLockService extends IService<KingdeeBillLock> {
*/
void checkEditable(String sourceBillType, Long sourceBillId);
/**
* 结算调整重推解锁:把源单当前「锁定中」的锁记录标记为 unlocked(非金蝶回调,仅本地解锁留痕),
* 供原结算单被调整重推生成新单后解除旧锁使用;新单另行落新锁记录
*
* @param sourceBillType WLTMS 单据类型
* @param sourceBillId WLTMS 业务主键
* @param operator 操作人
* @return 解锁的记录数
*/
int unlockByAdjustRepush(String sourceBillType, Long sourceBillId, String operator);
/**
* 批量查源单当前锁状态(供列表/详情页填充展示标识):
* 每源单取最新一条非终态锁记录(locked/failed),unlocked 为终态不返回(前端视为无锁/已解锁)
@@ -251,6 +251,28 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public int unlockByAdjustRepush(String sourceBillType, Long sourceBillId, String operator) {
List<KingdeeBillLock> lockedRecords = this.list(Wrappers.<KingdeeBillLock>lambdaQuery()
.eq(KingdeeBillLock::getSourceBillType, sourceBillType)
.eq(KingdeeBillLock::getSourceBillId, sourceBillId)
.eq(KingdeeBillLock::getLockStatus, KingdeeLockConstant.STATUS_LOCKED));
if (Func.isEmpty(lockedRecords)) {
return 0;
}
LocalDateTime unlockTime = LocalDateTime.now();
for (KingdeeBillLock lockRecord : lockedRecords) {
lockRecord.setLockStatus(KingdeeLockConstant.STATUS_UNLOCKED);
lockRecord.setOperationType(KingdeeLockConstant.OPERATION_ADJUST_REPUSH);
lockRecord.setUnlockTime(unlockTime);
lockRecord.setNotifyOperator(Func.isEmpty(operator) ? "system" : operator);
}
this.updateBatchById(lockedRecords);
log.info("结算调整重推解锁旧锁:源单[{}:{}],解锁{}张,操作人={}", sourceBillType, sourceBillId, lockedRecords.size(), operator);
return lockedRecords.size();
}
@Override
public void checkEditable(String sourceBillType, Long sourceBillId) {
long lockedCount = this.count(Wrappers.<KingdeeBillLock>lambdaQuery()
@@ -309,8 +331,11 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
}
row.setKingdeeUnlockTime(unlockRecord.getUnlockTime());
row.setKingdeeUnlockOperator(unlockRecord.getNotifyOperator());
row.setKingdeeUnlockOperationName(KingdeeLockConstant.OPERATION_DELETE.equals(unlockRecord.getOperationType())
? "金蝶删单" : "金蝶锁单");
row.setKingdeeUnlockOperationName(switch (Func.toStr(unlockRecord.getOperationType())) {
case KingdeeLockConstant.OPERATION_DELETE -> "金蝶删单";
case KingdeeLockConstant.OPERATION_ADJUST_REPUSH -> "结算调整重推";
default -> "金蝶锁单";
});
}
}
@@ -4,12 +4,18 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.system.cache.UserCache;
import org.springblade.transport.constant.KingdeeBfiConstant;
import org.springblade.transport.constant.TransportBusiConstant;
import org.springblade.transport.event.KingdeeBillApprovedEvent;
import org.springblade.transport.support.KingdeeApBillSyncSupport;
import org.springblade.transport.support.KingdeeArBillSyncSupport;
import org.springblade.transport.mapper.FormalSettlementDetailFeeMapper;
import org.springblade.transport.mapper.FormalSettlementDetailMapper;
import org.springblade.transport.mapper.FormalSettlementChangeRecordMapper;
@@ -33,7 +39,9 @@ import org.springblade.transport.pojo.entity.PaymentApplicationSettlement;
import org.springblade.transport.pojo.entity.SettlementAdjustment;
import org.springblade.transport.pojo.entity.SettlementAdjustmentDetail;
import org.springblade.transport.pojo.vo.SettlementAdjustmentVO;
import org.springblade.transport.service.IFormalSettlementService;
import org.springblade.transport.service.ISettlementAdjustmentService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -51,6 +59,7 @@ import java.util.Set;
@Service
@RequiredArgsConstructor
@Slf4j
public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementAdjustmentMapper, SettlementAdjustment>
implements ISettlementAdjustmentService {
private static final String PAID = "paid";
@@ -63,6 +72,10 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
private final FormalSettlementPaymentMapper formalPaymentMapper;
private final PaymentApplicationMapper paymentApplicationMapper;
private final PaymentApplicationSettlementMapper paymentApplicationSettlementMapper;
private final IFormalSettlementService formalSettlementService;
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
private final KingdeeArBillSyncSupport kingdeeArBillSyncSupport;
private final ApplicationEventPublisher eventPublisher;
@Override
public IPage<SettlementAdjustmentVO> selectPage(IPage<SettlementAdjustment> page, SettlementAdjustmentVO query) {
@@ -204,24 +217,140 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
}
@Override public void returnBill(SettlementAdjustmentStatusRequest request) { changeStatus(request.getId(), TransportBusiConstant.REVIEWING, TransportBusiConstant.RETURNED, "已驳回", limit(request.getReason(), 200)); }
/**
* 重新推送(高危):原单已推送金蝶且旧单已在金蝶冲销后,克隆新正式结算单并应用调整,
* 以新单号组成新防重键生成全新应付/应收单,调整单改挂新单、原单标记已调整并解锁旧锁。
* <p>
* 非事务编排:克隆+应用(独立事务)→ 推送(外部远程)→ 收尾回填,推送失败不回滚克隆,
* 重试时按来源原单号幂等复用已克隆新单,从推送步骤续上。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String repush(Long adjustmentId) {
SettlementAdjustment adjustment = existing(adjustmentId);
if (!TransportBusiConstant.APPROVED.equals(adjustment.getApprovalStatus())) throw new ServiceException("仅审批通过的结算调整单允许重新推送");
FormalSettlement formal = formalMapper.selectById(adjustment.getFormalSettlementId());
if (formal == null || !"synced".equals(formal.getKingdeeSyncStatus())) throw new ServiceException("关联正式结算单尚未推送金蝶,无需重新推送");
String kingdeeNo = "K3AP" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
formal.setKingdeeBillNo(kingdeeNo); formal.setSyncedTime(LocalDateTime.now()); formalMapper.updateById(formal);
return kingdeeNo;
if (!TransportBusiConstant.APPROVED.equals(adjustment.getApprovalStatus())) {
throw new ServiceException("仅审批通过的结算调整单允许重新推送");
}
FormalSettlement original = formalMapper.selectById(adjustment.getFormalSettlementId());
if (original == null) {
throw new ServiceException("关联正式结算单不存在");
}
if (!KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(original.getKingdeeSyncStatus())) {
throw new ServiceException("关联正式结算单尚未推送金蝶,请先执行「推送金蝶」");
}
// 冲销硬闸门:仅当金蝶侧原应付/应收单已被冲销(部分冲销放行)才允许以新单号重推,避免重复生成财务单据
if (!this.kingdeeWrittenOff(original)) {
throw new ServiceException("原应付/应收单尚未在金蝶冲销,禁止重推");
}
// 幂等:同一原单已克隆过新单则复用(重试从推送步骤续上),避免重复建单
FormalSettlement clone = existingClone(original.getFormalSettlementNo());
if (clone == null) {
Long cloneId = self().cloneAndApplyForRepush(original.getId(), adjustment.getId());
clone = formalMapper.selectById(cloneId);
}
// 推送新单:新单号天然组成新防重键,内部完成 A102-2/A302-2 推送 + 落锁 + 附件上传
// shortcut: 成功后若再次点击重推,调整单已改挂新单且新单未冲销,会被冲销闸门拦截(无副作用);如需彻底隐藏由前端按「关联单是否克隆单」判断
String billNo = formalSettlementService.syncKingdee(clone.getId());
// 成功收尾:调整单改挂新单 + 回填金蝶单号/同步状态;原单标记已调整 + 旧锁解锁(业务数据不动)
SettlementAdjustment patch = new SettlementAdjustment();
patch.setId(adjustment.getId());
patch.setFormalSettlementId(clone.getId());
patch.setFormalSettlementNo(clone.getFormalSettlementNo());
patch.setKingdeeSyncStatus(KingdeeBfiConstant.KINGDEE_STATUS_SYNCED);
patch.setKingdeeBillNo(billNo);
updateById(patch);
formalSettlementService.markSettlementAdjusted(original.getId(), adjustment.getAdjustmentNo(), clone.getFormalSettlementNo());
return billNo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void approve(SettlementAdjustmentStatusRequest request) {
public Long cloneAndApplyForRepush(Long originalSettlementId, Long adjustmentId) {
IFormalSettlementService.CloneResult cloneResult = formalSettlementService.cloneFormalSettlement(originalSettlementId);
FormalSettlement clone = formalMapper.selectById(cloneResult.settlementId());
applyAdjustmentToSettlement(clone, existing(adjustmentId), cloneResult.detailIdMap(), cloneResult.feeIdMap());
return cloneResult.settlementId();
}
@Override
public String syncKingdee(Long adjustmentId) {
SettlementAdjustment adjustment = existing(adjustmentId);
if (!TransportBusiConstant.APPROVED.equals(adjustment.getApprovalStatus())) {
throw new ServiceException("仅审批通过的结算调整单允许推送金蝶");
}
// 未推送/生成失败场景:推送关联正式结算单,成功后由正式结算单回填调整单金蝶单号与同步状态
return formalSettlementService.syncKingdee(adjustment.getFormalSettlementId());
}
@Override
public Map<String, Object> kingdeeWriteOffPrecheck(Long formalSettlementId) {
Map<String, Object> result = new HashMap<>();
FormalSettlement settlement = formalMapper.selectById(formalSettlementId);
if (settlement == null) {
throw new ServiceException("关联正式结算单不存在");
}
if (!KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(settlement.getKingdeeSyncStatus())) {
result.put("syncStatus", KingdeeBfiConstant.KINGDEE_STATUS_UNSYNCED);
result.put("writtenOff", null);
result.put("message", "该结算单尚未推送金蝶,审批通过后将自动推送原单");
return result;
}
result.put("syncStatus", KingdeeBfiConstant.KINGDEE_STATUS_SYNCED);
try {
boolean writtenOff = this.kingdeeWrittenOff(settlement);
result.put("writtenOff", writtenOff);
result.put("message", writtenOff
? "该结算单已推送金蝶且旧单已冲销,审批后可直接重新推送新单"
: "该结算单已推送金蝶且旧单尚未冲销,审批通过后需先由财务在金蝶完成冲销,才能重新推送");
} catch (Exception exception) {
// 预检为软提示:金蝶查询异常降级为「未校验」,不阻断建单,由审批分流与重推闸门兜底
log.warn("结算调整单建单预检:金蝶冲销状态查询失败,降级未校验,正式结算单[{}]",
settlement.getFormalSettlementNo(), exception);
result.put("writtenOff", null);
result.put("message", "金蝶冲销状态查询失败,未校验;审批后请以实际冲销情况为准");
}
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public String approve(SettlementAdjustmentStatusRequest request) {
SettlementAdjustment adjustment = existing(request.getId());
if (!TransportBusiConstant.REVIEWING.equals(adjustment.getApprovalStatus())) throw new ServiceException("仅审批中的结算调整单允许审核");
FormalSettlement formal = adjustableFormalSettlement(adjustment.getFormalSettlementId());
boolean synced = KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(formal.getKingdeeSyncStatus());
if (synced) {
// 已推送场景:原单业务数据零改动,仅追加 1 条重推提示变更记录,待财务在金蝶手工冲销旧单后重推
saveFormalChange(formal.getId(), "合计费用项", null, "调整",
"结算调整单" + adjustment.getAdjustmentNo() + "审批通过,调整金额【" + money(adjustment.getAdjustmentAmount()) + "】,待重推金蝶",
adjustment.getRemark());
} else {
// 未推送场景:回写原单费用/金额并重算结算金额,事务提交后由事件监听自动推送金蝶
applyAdjustmentToSettlement(formal, adjustment, Map.of(), Map.of());
saveFormalChange(formal.getId(), "合计费用项", null, "调整",
"调整单" + adjustment.getAdjustmentNo() + "调整金额【" + money(adjustment.getAdjustmentAmount()) + "】",
adjustment.getRemark());
}
adjustment.setKingdeeSyncStatus(formal.getKingdeeSyncStatus()); adjustment.setApprovalStatus(TransportBusiConstant.APPROVED); adjustment.setCurrentNode("审批通过"); adjustment.setCurrentProcessor(AuthUtil.getUserName()); adjustment.setApprovedTime(LocalDateTime.now()); updateById(adjustment);
if (!synced) {
// 审批事务提交后(AFTER_COMMIT)自动推原单,成功回填调整单 synced + 金蝶单号、失败置 failed
eventPublisher.publishEvent(new KingdeeBillApprovedEvent(
KingdeeBillApprovedEvent.BIZ_FORMAL_SETTLEMENT, formal.getId()));
return null;
}
return "原结算单已推送金蝶,请财务人员在金蝶手工完成旧应付/应收单冲销处理后再重新推送";
}
/**
* 应用调整到目标正式结算单(未推送场景作用于原单,重推场景作用于克隆新单):
* 回写明细费项金额、汇总费用项、重算明细与结算金额;明细/费项 id 经映射转换(原单调整作用于原单时传空映射)。
*
* @param formal 目标正式结算单(改动后需落库)
* @param adjustment 结算调整单
* @param detailIdMap 原结算明细id -&gt; 目标结算明细id(原单场景传空 Map)
* @param feeIdMap 原明细费项id -&gt; 目标明细费项id(原单场景传空 Map)
*/
private void applyAdjustmentToSettlement(FormalSettlement formal, SettlementAdjustment adjustment,
Map<Long, Long> detailIdMap, Map<Long, Long> feeIdMap) {
for (SettlementAdjustmentDetail item : detailMapper.selectList(Wrappers.<SettlementAdjustmentDetail>lambdaQuery().eq(SettlementAdjustmentDetail::getAdjustmentId, adjustment.getId()))) {
if (money(item.getAdjustmentAmountTax()).signum() == 0) continue;
if (isManualFee(item)) {
@@ -229,12 +358,14 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
"新增【" + item.getFeeItem() + "】调整费用【" + money(item.getAdjustmentAmountTax()) + "】",
Func.isEmpty(item.getRemark()) ? adjustment.getRemark() : item.getRemark());
} else {
FormalSettlementDetailFee fee = validateFee(formal.getId(), item.getFormalSettlementDetailId(), item.getFormalSettlementDetailFeeId());
Long detailId = detailIdMap.getOrDefault(item.getFormalSettlementDetailId(), item.getFormalSettlementDetailId());
Long feeId = feeIdMap.getOrDefault(item.getFormalSettlementDetailFeeId(), item.getFormalSettlementDetailFeeId());
FormalSettlementDetailFee fee = validateFee(formal.getId(), detailId, feeId);
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());
FormalSettlementDetail formalDetail = formalDetailMapper.selectById(detailId);
saveFormalChange(formal.getId(), "结算明细项", formalDetail == null ? null : formalDetail.getLineNo(),
"调整", "【" + (Func.isEmpty(item.getFeeItem()) ? "结算费用" : item.getFeeItem())
+ "】从【" + beforeAmount + "】调整为【" + fee.getSettlementAmountTax() + "】",
@@ -251,13 +382,36 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpl<SettlementA
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.getPaidAmount()))
.max(BigDecimal.ZERO));
formal.setRemainingPayableAmount(amount.subtract(money(formal.getPaidAmount())).max(BigDecimal.ZERO));
formalMapper.updateById(formal);
saveFormalChange(formal.getId(), "合计费用项", null, "调整",
"调整单" + adjustment.getAdjustmentNo() + "调整金额【" + money(adjustment.getAdjustmentAmount()) + "】",
adjustment.getRemark());
adjustment.setKingdeeSyncStatus(formal.getKingdeeSyncStatus()); adjustment.setApprovalStatus(TransportBusiConstant.APPROVED); adjustment.setCurrentNode("审批通过"); adjustment.setCurrentProcessor(AuthUtil.getUserName()); adjustment.setApprovedTime(LocalDateTime.now()); updateById(adjustment);
}
/** 涉事事务的自代理:跨方法调用确保 {@link #cloneAndApplyForRepush} 的事务注解生效 */
private ISettlementAdjustmentService self() {
return SpringUtil.getBean(ISettlementAdjustmentService.class);
}
/** 按来源原正式结算单号查已克隆新单(重推幂等:同一原单不重复建单) */
private FormalSettlement existingClone(String originFormalSettlementNo) {
List<FormalSettlement> clones = formalMapper.selectList(Wrappers.<FormalSettlement>lambdaQuery()
.eq(FormalSettlement::getOriginFormalSettlementNo, originFormalSettlementNo)
.orderByDesc(FormalSettlement::getCreateTime));
return clones.isEmpty() ? null : clones.get(0);
}
/** 按原单旧防重键(原结算单号_明细单据号)查金蝶是否已冲销(应付 A102-4 / 应收 A302-4,部分冲销放行) */
private boolean kingdeeWrittenOff(FormalSettlement settlement) {
List<FormalSettlementDetail> details = formalDetailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
.eq(FormalSettlementDetail::getFormalSettlementId, settlement.getId())
.eq(FormalSettlementDetail::getIsDeleted, 0)
.orderByAsc(FormalSettlementDetail::getLineNo));
if (Func.isEmpty(details)) {
return false;
}
if ("receivable".equals(settlement.getSettlementType())) {
return kingdeeArBillSyncSupport.queryArBillWrittenOff(kingdeeArBillSyncSupport.arBillSrcCode(settlement, details));
}
return kingdeeApBillSyncSupport.queryApBillWrittenOff(kingdeeApBillSyncSupport.apBillSrcCode(settlement, details));
}
private void saveFormalChange(Long settlementId, String changeType, Integer lineNo,