🐛 修复收款流水继续认领与付款可付口径,并改用OA登录名提交流程
收款流水部分认领预填关联结算单并修正认领完成判定;正式结算尾款扣减预付占用;预付驳回重提纠正结算引用;临时额度取消强制项目额度校验;用户增加OA登录名供MK流程使用。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+6
@@ -39,6 +39,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 收款流水视图实体类
|
||||
@@ -91,4 +92,9 @@ public class ReceiptFlowVO extends KingdeeReceiptFlow {
|
||||
private BigDecimal receiptAmountEnd;
|
||||
@TableField(exist = false)
|
||||
private List<ReceiptFlowRecord> claimRecords = new ArrayList<>();
|
||||
/**
|
||||
* 本流水历史关联且仍有剩余可认领金额的应收正式结算单(继续认领时预填)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<Map<String, Object>> relatedSettlements = new ArrayList<>();
|
||||
}
|
||||
|
||||
+4
@@ -132,6 +132,10 @@ public class User extends TenantEntity implements IPhone {
|
||||
* 是否是oa的账号
|
||||
*/
|
||||
private Integer isOa;
|
||||
/**
|
||||
* OA登录名
|
||||
*/
|
||||
private String oaLoginId;
|
||||
/**
|
||||
* OA上次更新时间(OA 的 modified 字段),仅同步比对使用
|
||||
*/
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.process.pojo.dto.ApprovalDTO;
|
||||
@@ -49,7 +48,6 @@ public class BusinessProcessController extends BladeController {
|
||||
if (param == null) {
|
||||
param = new ApprovalDTO();
|
||||
}
|
||||
param.setLoginName(AuthUtil.getUserAccount());
|
||||
IPage<ApprovalVO> pages = businessProcessService.queryMkApprovalList(Condition.getPage(query), param);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ public interface IBusinessProcessService extends IService<BusinessProcess> {
|
||||
* 获取流程当前节点详情
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param loginName MK登录名(手机号),可为空,为空时按流程发起人解析
|
||||
* @param loginName MK登录名(OA登录名),可为空,为空时按流程发起人解析
|
||||
* @return 当前节点列表
|
||||
*/
|
||||
List<?> getCurrentNodes(String processInstanceId, String loginName);
|
||||
@@ -80,7 +80,7 @@ public interface IBusinessProcessService extends IService<BusinessProcess> {
|
||||
* 获取流程实例详情
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param loginName MK登录名(手机号),可为空,为空时按流程发起人解析
|
||||
* @param loginName MK登录名(OA登录名),可为空,为空时按流程发起人解析
|
||||
* @return 流程实例详情
|
||||
*/
|
||||
Object getProcessInfo(String processInstanceId, String loginName);
|
||||
|
||||
+21
-27
@@ -132,9 +132,9 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
public String processSubmit(MKProcessCreateDTO param) {
|
||||
AssertUtils.notNull(param, "提交流程参数不能为空");
|
||||
AssertUtils.notBlank(param.getTemplateCode(), "模板编码不能为空");
|
||||
// 前端拿到的手机号可能经 @Sensitive 脱敏(如 137****8880),这里从库取真实手机号覆盖
|
||||
String loginName = resolveCurrentUserPhone();
|
||||
AssertUtils.notBlank(loginName, "当前用户手机号为空,无法提交审核流");
|
||||
// MK loginName 使用当前用户 OA 登录名,未配置则不允许提交
|
||||
String loginName = resolveCurrentUserOaLoginId();
|
||||
AssertUtils.notBlank(loginName, "当前用户OA登录名为空,无法提交审核流");
|
||||
param.setSubmitIdentity(loginName);
|
||||
param.setLoginName(loginName);
|
||||
String bizType = param.getBizType();
|
||||
@@ -258,7 +258,7 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
resolvedLoginName = businessProcess.getPromoterLoginName();
|
||||
}
|
||||
if (StringUtil.isBlank(resolvedLoginName)) {
|
||||
resolvedLoginName = resolveCurrentUserPhone();
|
||||
resolvedLoginName = resolveCurrentUserOaLoginId();
|
||||
}
|
||||
if (StringUtil.isBlank(resolvedLoginName)) {
|
||||
return false;
|
||||
@@ -291,10 +291,10 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
}
|
||||
String loginName = businessProcess.getPromoterLoginName();
|
||||
if (StringUtil.isBlank(loginName)) {
|
||||
loginName = resolveCurrentUserPhone();
|
||||
loginName = resolveCurrentUserOaLoginId();
|
||||
}
|
||||
if (StringUtil.isBlank(loginName)) {
|
||||
log.warn("查询MK流程实例跳过,登录名为空 bizId={} processInstanceId={}",
|
||||
log.warn("查询MK流程实例跳过,OA登录名为空 bizId={} processInstanceId={}",
|
||||
bizId, businessProcess.getProcessInstanceId());
|
||||
return false;
|
||||
}
|
||||
@@ -357,9 +357,9 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
}
|
||||
String loginName = businessProcess.getPromoterLoginName();
|
||||
if (StringUtil.isBlank(loginName)) {
|
||||
loginName = resolveCurrentUserPhone();
|
||||
loginName = resolveCurrentUserOaLoginId();
|
||||
}
|
||||
AssertUtils.notBlank(loginName, "当前用户手机号为空,无法删除审核流");
|
||||
AssertUtils.notBlank(loginName, "当前用户OA登录名为空,无法删除审核流");
|
||||
log.info("调用mk processDelete processInstanceId={} loginName={} bizId={} formInstanceId={}",
|
||||
businessProcess.getProcessInstanceId(), loginName, bizId, businessProcess.getFormInstanceId());
|
||||
boolean deleted = mkService.processDelete(businessProcess.getProcessInstanceId(), loginName);
|
||||
@@ -563,27 +563,18 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前登录用户实体读取真实手机号(库内 SM2 密文先解密,绕过接口脱敏)
|
||||
* 从当前登录用户实体读取 OA 登录名,作为 MK 接口 loginName
|
||||
*/
|
||||
private String resolveCurrentUserPhone() {
|
||||
private String resolveCurrentUserOaLoginId() {
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (userId != null) {
|
||||
User user = userService.getById(userId);
|
||||
if (user != null) {
|
||||
userService.decryptPhone(user);
|
||||
if (StringUtil.isNotBlank(user.getPhone()) && !user.getPhone().contains("*")) {
|
||||
return user.getPhone().trim();
|
||||
}
|
||||
if (StringUtil.isNotBlank(user.getAccount()) && user.getAccount().matches("^1\\d{10}$")) {
|
||||
return user.getAccount().trim();
|
||||
}
|
||||
}
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
String account = AuthUtil.getUserAccount();
|
||||
if (StringUtil.isNotBlank(account) && account.matches("^1\\d{10}$")) {
|
||||
return account.trim();
|
||||
User user = userService.getById(userId);
|
||||
if (user == null || StringUtil.isBlank(user.getOaLoginId())) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return user.getOaLoginId().trim();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -703,8 +694,8 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
processInstanceId = businessProcess.getProcessInstanceId();
|
||||
}
|
||||
try {
|
||||
String loginName = resolveCurrentUserPhone();
|
||||
AssertUtils.notBlank(loginName, "当前用户手机号为空,无法获取审核跳转地址");
|
||||
String loginName = resolveCurrentUserOaLoginId();
|
||||
AssertUtils.notBlank(loginName, "当前用户OA登录名为空,无法获取审核跳转地址");
|
||||
String mkApprovalUrl = mkService.getMKApprovalUrl(processInstanceId, loginName);
|
||||
AssertUtils.notBlank(mkApprovalUrl, "获取mk审批页面链接异常");
|
||||
return mkApprovalUrl;
|
||||
@@ -921,6 +912,9 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
|
||||
@Override
|
||||
public IPage<ApprovalVO> queryMkApprovalList(IPage<ApprovalVO> page, ApprovalDTO param) {
|
||||
String loginName = resolveCurrentUserOaLoginId();
|
||||
AssertUtils.notBlank(loginName, "当前用户OA登录名为空,无法查询审核列表");
|
||||
param.setLoginName(loginName);
|
||||
if (MKDoc.RELATED.getCode().equals(param.getDocType())) {
|
||||
// 我参与的,调用流程列表接口
|
||||
MKProcessDTO processParam = approvalConvert.dto2mk(param);
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ public interface UserConvert {
|
||||
@Mapping(source = "lastname", target = "realName")
|
||||
@Mapping(source = "mobile", target = "phone")
|
||||
@Mapping(source = "email", target = "email")
|
||||
@Mapping(source = "loginid", target = "oaLoginId")
|
||||
@Mapping(source = "modified", target = "oaModified")
|
||||
User baseConvert(OAPersonResponse person);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<result column="dept_id" property="deptId"/>
|
||||
<result column="post_id" property="postId"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="oa_login_id" property="oaLoginId"/>
|
||||
<result column="oa_person_id" property="oaPersonId"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
+1
@@ -366,6 +366,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
user.setPostId(null);
|
||||
user.setAccount(null);
|
||||
user.setIsLeader(null);
|
||||
user.setOaLoginId(null);
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
return submitUserInfo(user);
|
||||
}
|
||||
|
||||
+4
-2
@@ -1729,7 +1729,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
Map<Long, BigDecimal> receiptClaimPaidBySettlement = hasReceivable
|
||||
? receiptClaimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.in(ReceiptClaimSettlement::getFormalSettlementId, settledIds)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)).stream()
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
|
||||
.collect(Collectors.groupingBy(ReceiptClaimSettlement::getFormalSettlementId,
|
||||
Collectors.reducing(BigDecimal.ZERO,
|
||||
item -> money(item.getAllocatedReceiptAmount()), BigDecimal::add)))
|
||||
@@ -2161,7 +2162,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
: receiptClaimSettlementMapper.selectList(
|
||||
Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getFormalSettlementId, settlement.getId())
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1))
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0))
|
||||
.stream().map(ReceiptClaimSettlement::getAllocatedReceiptAmount).map(this::money)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
+124
-20
@@ -321,11 +321,15 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
BigDecimal writeOffAmount = preSettlementReference
|
||||
? projectAdvanceWriteOffService.sumBySettlement(TransportBusiConstant.SETTLEMENT_BILL_PRE, referenceId)
|
||||
: projectAdvanceWriteOffService.sumBySettlement(TransportBusiConstant.SETTLEMENT_BILL_FORMAL, referenceId);
|
||||
// 正式结算尾款还需扣减来源预结算进度预付(与正式结算剩余应付口径一致)
|
||||
BigDecimal advanceOccupied = preSettlementReference
|
||||
? BigDecimal.ZERO
|
||||
: formalSettlementAdvanceOccupied(referenceId);
|
||||
BigDecimal occupiedTotal = cumulativeAppliedAmount.add(writeOffAmount).add(advanceOccupied);
|
||||
PaymentApplicationReferenceAmountVO amount = new PaymentApplicationReferenceAmountVO();
|
||||
amount.setSettlementAmount(settlementAmount);
|
||||
amount.setCumulativeAppliedAmount(cumulativeAppliedAmount.add(writeOffAmount));
|
||||
amount.setPayableAmount(settlementAmount.subtract(cumulativeAppliedAmount).subtract(writeOffAmount)
|
||||
.max(BigDecimal.ZERO));
|
||||
amount.setCumulativeAppliedAmount(occupiedTotal);
|
||||
amount.setPayableAmount(settlementAmount.subtract(occupiedTotal).max(BigDecimal.ZERO));
|
||||
amount.setPaymentSequence(resolvePaymentSequence(preSettlementReference, referenceId, excludeId));
|
||||
return amount;
|
||||
}
|
||||
@@ -1891,7 +1895,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
}
|
||||
|
||||
/**
|
||||
* 进度预付只认预结算单。尾款付款若同时带回一个并不存在的正式结算单 ID,改为按预结算单保存。
|
||||
* 进度预付只认预结算单;尾款付款若 settlementId 实际是预结算单 ID 或正式结算单已不存在,改为按预结算单保存。
|
||||
*/
|
||||
private void normalizeSettlementReference(PaymentApplicationSaveRequest request) {
|
||||
if ("progress_advance".equals(request.getPaymentType())) {
|
||||
@@ -1901,33 +1905,62 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
if (request.getPreSettlementId() == null) {
|
||||
request.setPreSettlementId(request.getSettlementId());
|
||||
}
|
||||
if (request.getPreSettlementId() == null && request.getSettlementIds() != null) {
|
||||
request.setPreSettlementId(request.getSettlementIds().stream().filter(Objects::nonNull).findFirst().orElse(null));
|
||||
}
|
||||
request.setSettlementId(null);
|
||||
request.setSettlementIds(null);
|
||||
return;
|
||||
}
|
||||
if (!TransportBusiConstant.PAYMENT_TYPE_SETTLEMENT.equals(request.getPaymentType()) || request.getSettlementId() == null) {
|
||||
if (!TransportBusiConstant.PAYMENT_TYPE_SETTLEMENT.equals(request.getPaymentType())) {
|
||||
return;
|
||||
}
|
||||
boolean hasPreSettlement = request.getPreSettlementId() != null
|
||||
|| (request.getPreSettlementIds() != null && request.getPreSettlementIds().stream().anyMatch(Objects::nonNull));
|
||||
if (!hasPreSettlement) {
|
||||
// settlementIds 中若含无效正式结算单 ID,先尝试纠正为预结算
|
||||
if (request.getSettlementId() == null && request.getSettlementIds() != null
|
||||
&& !request.getSettlementIds().isEmpty()) {
|
||||
request.setSettlementId(request.getSettlementIds().stream().filter(Objects::nonNull).findFirst().orElse(null));
|
||||
}
|
||||
if (request.getSettlementId() == null) {
|
||||
return;
|
||||
}
|
||||
FormalSettlement formalSettlement = formalSettlementMapper.selectById(request.getSettlementId());
|
||||
if (formalSettlement == null || Objects.equals(formalSettlement.getIsDeleted(), 1)) {
|
||||
if (formalSettlement != null && !Objects.equals(formalSettlement.getIsDeleted(), 1)) {
|
||||
return;
|
||||
}
|
||||
// 正式结算单不存在:若该 ID 实为预结算单,或请求已带预结算引用,则清空正式结算引用
|
||||
PreSettlement preSettlement = preSettlementMapper.selectById(request.getSettlementId());
|
||||
boolean isPreSettlement = preSettlement != null && !Objects.equals(preSettlement.getIsDeleted(), 1);
|
||||
boolean hasPreSettlement = request.getPreSettlementId() != null
|
||||
|| (request.getPreSettlementIds() != null && request.getPreSettlementIds().stream().anyMatch(Objects::nonNull));
|
||||
if (isPreSettlement && request.getPreSettlementId() == null) {
|
||||
request.setPreSettlementId(request.getSettlementId());
|
||||
if (request.getPreSettlementIds() == null || request.getPreSettlementIds().isEmpty()) {
|
||||
request.setPreSettlementIds(List.of(request.getSettlementId()));
|
||||
}
|
||||
}
|
||||
if (isPreSettlement || hasPreSettlement) {
|
||||
request.setSettlementId(null);
|
||||
request.setSettlementIds(null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 预结算付款不挂正式结算单时,清掉历史误写入的正式结算单 ID,避免再次提交按正式结算单刷新。 */
|
||||
/**
|
||||
* 预结算付款不挂正式结算单时,清掉历史误写入的正式结算单 ID,避免再次提交按正式结算单刷新。
|
||||
*/
|
||||
private void clearDetachedFormalReference(PaymentApplication entity) {
|
||||
if (entity.getId() == null || entity.getPreSettlementId() == null || entity.getSettlementId() != null) {
|
||||
if (entity.getId() == null) {
|
||||
return;
|
||||
}
|
||||
boolean progressAdvance = "progress_advance".equals(entity.getPaymentType());
|
||||
boolean preOnly = entity.getPreSettlementId() != null && entity.getSettlementId() == null;
|
||||
if (!progressAdvance && !preOnly) {
|
||||
return;
|
||||
}
|
||||
baseMapper.update(null, Wrappers.<PaymentApplication>lambdaUpdate()
|
||||
.eq(PaymentApplication::getId, entity.getId())
|
||||
.setSql("settlement_id = null, settlement_no = null"));
|
||||
entity.setSettlementId(null);
|
||||
entity.setSettlementNo(null);
|
||||
}
|
||||
|
||||
private void fillReference(PaymentApplication entity, PaymentApplicationSaveRequest request) {
|
||||
@@ -1938,15 +1971,26 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
entity.setProjectId(request.getProjectId()); entity.setProjectName(request.getProjectName()); entity.setDeptId(request.getDeptId()); entity.setDeptName(request.getDeptName()); entity.setContractId(request.getContractId()); entity.setContractNo(request.getContractNo()); entity.setContractName(request.getContractName()); entity.setPayerName(request.getPayerName()); entity.setPayeeName(request.getPayeeName()); entity.setSettlementAmount(request.getSettlementAmount()); entity.setPayableAmount(money(project.getFundDemand()).multiply(BigDecimal.valueOf(10000))); entity.setBillType(Func.isEmpty(project.getSettlementMode()) ? null : "项目预付");
|
||||
} else if (request.getSettlementId() != null) {
|
||||
FormalSettlement source = formalSettlementMapper.selectById(request.getSettlementId());
|
||||
if (source == null) throw new ServiceException("正式结算单不存在");
|
||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) throw new ServiceException("只能选择审批通过、未作废的正式结算单");
|
||||
entity.setSettlementId(source.getId()); entity.setSettlementNo(source.getFormalSettlementNo()); entity.setPreSettlementId(null); entity.setPreSettlementNo(null);
|
||||
entity.setProjectId(source.getProjectId()); entity.setProjectName(source.getProjectName()); entity.setDeptId(source.getDeptId()); entity.setDeptName(source.getDeptName()); entity.setContractId(source.getContractId()); entity.setContractNo(source.getContractNo()); entity.setContractName(source.getContractName()); entity.setPayerName(source.getPayerName()); entity.setPayeeName(source.getPayeeName()); entity.setSettlementAmount(source.getSettlementAmount()); entity.setPayableAmount(payableOfFormalSettlement(source.getId(), source.getSettlementAmount(), entity.getId())); entity.setBillType("正式结算单");
|
||||
if (source == null || Objects.equals(source.getIsDeleted(), 1)) {
|
||||
// 驳回重提等场景:settlementId 可能误存为预结算单 ID,降级走预结算分支
|
||||
PreSettlement preSettlement = preSettlementMapper.selectById(request.getSettlementId());
|
||||
if (preSettlement != null && !Objects.equals(preSettlement.getIsDeleted(), 1)) {
|
||||
request.setPreSettlementId(preSettlement.getId());
|
||||
request.setSettlementId(null);
|
||||
fillPreSettlementReference(entity, preSettlement);
|
||||
} else if (request.getPreSettlementId() != null) {
|
||||
request.setSettlementId(null);
|
||||
fillPreSettlementReference(entity, requireApprovedPreSettlement(request.getPreSettlementId()));
|
||||
} else {
|
||||
throw new ServiceException("正式结算单不存在");
|
||||
}
|
||||
} else {
|
||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) throw new ServiceException("只能选择审批通过、未作废的正式结算单");
|
||||
entity.setSettlementId(source.getId()); entity.setSettlementNo(source.getFormalSettlementNo()); entity.setPreSettlementId(null); entity.setPreSettlementNo(null);
|
||||
entity.setProjectId(source.getProjectId()); entity.setProjectName(source.getProjectName()); entity.setDeptId(source.getDeptId()); entity.setDeptName(source.getDeptName()); entity.setContractId(source.getContractId()); entity.setContractNo(source.getContractNo()); entity.setContractName(source.getContractName()); entity.setPayerName(source.getPayerName()); entity.setPayeeName(source.getPayeeName()); entity.setSettlementAmount(source.getSettlementAmount()); entity.setPayableAmount(payableOfFormalSettlement(source.getId(), source.getSettlementAmount(), entity.getId())); entity.setBillType("正式结算单");
|
||||
}
|
||||
} else if (request.getPreSettlementId() != null) {
|
||||
PreSettlement source = preSettlementMapper.selectById(request.getPreSettlementId());
|
||||
if (source == null) throw new ServiceException("预结算单不存在");
|
||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) throw new ServiceException("只能选择审批通过、未作废的预结算单");
|
||||
entity.setPreSettlementId(source.getId()); entity.setPreSettlementNo(source.getPreSettlementNo()); entity.setSettlementId(null); entity.setSettlementNo(null); entity.setProjectId(source.getProjectId()); entity.setProjectName(source.getProjectName()); entity.setDeptId(source.getDeptId()); entity.setDeptName(source.getDeptName()); entity.setContractId(source.getContractId()); entity.setContractNo(source.getContractNo()); entity.setContractName(source.getContractName()); entity.setPayerName(source.getPayerName()); entity.setPayeeName(source.getPayeeName()); entity.setSettlementAmount(source.getSettlementAmount()); entity.setPayableAmount(payableOfPreSettlement(source.getId(), source.getSettlementAmount(), entity.getId())); entity.setBillType("预结算单");
|
||||
fillPreSettlementReference(entity, requireApprovedPreSettlement(request.getPreSettlementId()));
|
||||
} else {
|
||||
entity.setProjectId(request.getProjectId()); entity.setProjectName(request.getProjectName()); entity.setDeptId(request.getDeptId()); entity.setDeptName(request.getDeptName()); entity.setContractId(request.getContractId()); entity.setContractNo(request.getContractNo()); entity.setContractName(request.getContractName()); entity.setPayerName(request.getPayerName()); entity.setPayeeName(request.getPayeeName()); entity.setSettlementAmount(request.getSettlementAmount()); entity.setPayableAmount(request.getPayableAmount()); entity.setBillType(request.getBillType());
|
||||
}
|
||||
@@ -1955,6 +1999,36 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
&& money(entity.getPayableAmount()).compareTo(BigDecimal.ZERO) > 0) throw new ServiceException("申请付款金额不能超过可付款金额");
|
||||
}
|
||||
|
||||
private PreSettlement requireApprovedPreSettlement(Long preSettlementId) {
|
||||
PreSettlement source = preSettlementMapper.selectById(preSettlementId);
|
||||
if (source == null || Objects.equals(source.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("预结算单不存在");
|
||||
}
|
||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) {
|
||||
throw new ServiceException("只能选择审批通过、未作废的预结算单");
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
private void fillPreSettlementReference(PaymentApplication entity, PreSettlement source) {
|
||||
entity.setPreSettlementId(source.getId());
|
||||
entity.setPreSettlementNo(source.getPreSettlementNo());
|
||||
entity.setSettlementId(null);
|
||||
entity.setSettlementNo(null);
|
||||
entity.setProjectId(source.getProjectId());
|
||||
entity.setProjectName(source.getProjectName());
|
||||
entity.setDeptId(source.getDeptId());
|
||||
entity.setDeptName(source.getDeptName());
|
||||
entity.setContractId(source.getContractId());
|
||||
entity.setContractNo(source.getContractNo());
|
||||
entity.setContractName(source.getContractName());
|
||||
entity.setPayerName(source.getPayerName());
|
||||
entity.setPayeeName(source.getPayeeName());
|
||||
entity.setSettlementAmount(source.getSettlementAmount());
|
||||
entity.setPayableAmount(payableOfPreSettlement(source.getId(), source.getSettlementAmount(), entity.getId()));
|
||||
entity.setBillType("预结算单");
|
||||
}
|
||||
|
||||
private List<FormalSettlement> resolveFormalSettlements(PaymentApplicationSaveRequest request, PaymentApplication entity) {
|
||||
if (!TransportBusiConstant.PAYMENT_TYPE_SETTLEMENT.equals(request.getPaymentType())) return List.of();
|
||||
List<Long> ids = request.getSettlementIds() == null ? List.of() : request.getSettlementIds();
|
||||
@@ -2358,15 +2432,45 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
return relationAmount.add(legacyAmount);
|
||||
}
|
||||
|
||||
/** 正式结算可付款 = 结算金额 − 已申请付款 − 项目预付核销 */
|
||||
/**
|
||||
* 正式结算可付款 = 结算金额 − 已申请付款 − 项目预付核销 − 来源预结算进度预付占用。
|
||||
* 进度预付占用与正式结算台账 remainingPayable 口径对齐,避免转正式后仍可按全额申请尾款。
|
||||
*/
|
||||
private BigDecimal payableOfFormalSettlement(Long settlementId, BigDecimal settlementAmount, Long excludeId) {
|
||||
return money(settlementAmount)
|
||||
.subtract(cumulativeFormalSettlementApplied(settlementId, excludeId))
|
||||
.subtract(projectAdvanceWriteOffService.sumBySettlement(
|
||||
TransportBusiConstant.SETTLEMENT_BILL_FORMAL, settlementId))
|
||||
.subtract(formalSettlementAdvanceOccupied(settlementId))
|
||||
.max(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正式结算来源预结算的进度预付占用合计:优先取来源行快照,快照为空时回退预结算实时已申请/已付。
|
||||
*/
|
||||
private BigDecimal formalSettlementAdvanceOccupied(Long settlementId) {
|
||||
if (settlementId == null) {
|
||||
return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
List<FormalSettlementSource> sources = formalSettlementSourceMapper.selectList(
|
||||
Wrappers.<FormalSettlementSource>lambdaQuery()
|
||||
.eq(FormalSettlementSource::getFormalSettlementId, settlementId)
|
||||
.eq(FormalSettlementSource::getIsDeleted, 0));
|
||||
if (sources.isEmpty()) {
|
||||
return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (FormalSettlementSource source : sources) {
|
||||
BigDecimal occupied = money(source.getAdvanceAppliedAmount()).max(money(source.getAdvancePaidAmount()));
|
||||
if (occupied.compareTo(BigDecimal.ZERO) <= 0 && source.getPreSettlementId() != null) {
|
||||
occupied = findPreSettlementApplied(source.getPreSettlementId())
|
||||
.max(findPreSettlementPaid(source.getPreSettlementId()));
|
||||
}
|
||||
total = total.add(occupied);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/** 预结算可付款 = 结算金额 − 已申请付款 − 项目预付核销 */
|
||||
private BigDecimal payableOfPreSettlement(Long preSettlementId, BigDecimal settlementAmount, Long excludeId) {
|
||||
return money(settlementAmount)
|
||||
|
||||
+20
-5
@@ -252,8 +252,8 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
BigDecimal flowClaimedAfter = money(flow.getClaimedAmount())
|
||||
.subtract(money(claim.getClaimAmount())).max(BigDecimal.ZERO);
|
||||
flow.setClaimedAmount(flowClaimedAfter);
|
||||
// 认领状态对照公告未认领金额重算(作废分摊不计入累计)
|
||||
flow.setClaimStatus(amountClaimStatus(flowClaimedAfter, flow.getUnclaimedAmount()));
|
||||
// 认领状态对照可认领上限重算(作废分摊不计入累计)
|
||||
flow.setClaimStatus(amountClaimStatus(flowClaimedAfter, claimCeiling(flow)));
|
||||
receiptFlowMapper.updateById(flow);
|
||||
|
||||
// 作废不生成金蝶认领冲单:金蝶单号/状态保留原值(未推送为空,已推送为真实收款单信息),
|
||||
@@ -411,13 +411,28 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
}
|
||||
|
||||
/**
|
||||
* 累计认领金额 → 流水认领状态:对照公告未认领金额判定(与收款流水侧口径一致)
|
||||
* 累计认领金额 → 流水认领状态:对照可认领上限判定(与收款流水侧口径一致)
|
||||
*/
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal unclaimedAmount) {
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal claimCeiling) {
|
||||
if (claimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return "unclaimed";
|
||||
}
|
||||
return claimedAmount.compareTo(money(unclaimedAmount)) >= 0 ? "claimed" : "partial";
|
||||
return claimedAmount.compareTo(money(claimCeiling)) >= 0 ? "claimed" : "partial";
|
||||
}
|
||||
|
||||
/**
|
||||
* 可认领上限:公告未认领金额与收款金额取较小正值(与收款流水侧口径一致)
|
||||
*/
|
||||
private BigDecimal claimCeiling(KingdeeReceiptFlow flow) {
|
||||
BigDecimal unclaimed = money(flow.getUnclaimedAmount());
|
||||
BigDecimal receipt = money(flow.getReceiptAmount());
|
||||
if (receipt.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return unclaimed;
|
||||
}
|
||||
if (unclaimed.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return unclaimed;
|
||||
}
|
||||
return unclaimed.min(receipt);
|
||||
}
|
||||
|
||||
private String amountStatus(BigDecimal paidAmount, BigDecimal settlementAmount) {
|
||||
|
||||
+126
-20
@@ -194,12 +194,16 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
.eq(ReceiptFlowRecord::getReceiptFlowId, id)
|
||||
.in(ReceiptFlowRecord::getActionType, List.of("claim", "push"))
|
||||
.orderByDesc(ReceiptFlowRecord::getCreateTime)));
|
||||
// 继续认领:带回本流水历史关联且仍有剩余可认领金额的结算单
|
||||
vo.setRelatedSettlements(relatedSettlementCandidates(id));
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> settlementCandidates(String keyword, Long flowId) {
|
||||
existing(flowId);
|
||||
// 本流水历史关联结算单优先保留(避免仅按创建时间 limit 截断后选不到)
|
||||
Set<Long> relatedIds = relatedSettlementIds(flowId);
|
||||
List<FormalSettlement> settlements = formalSettlementMapper.selectList(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.eq(FormalSettlement::getSettlementType, RECEIVABLE)
|
||||
@@ -210,7 +214,24 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
.or().like(FormalSettlement::getProjectName, keyword)
|
||||
.or().like(FormalSettlement::getContractName, keyword))
|
||||
.orderByDesc(FormalSettlement::getCreateTime)
|
||||
.last("limit 200"));
|
||||
.last("limit 500"));
|
||||
if (!relatedIds.isEmpty()) {
|
||||
Set<Long> loadedIds = settlements.stream().map(FormalSettlement::getId)
|
||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
List<Long> missingIds = relatedIds.stream().filter(id -> !loadedIds.contains(id)).toList();
|
||||
if (!missingIds.isEmpty()) {
|
||||
List<FormalSettlement> relatedSettlements = formalSettlementMapper.selectList(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.in(FormalSettlement::getId, missingIds)
|
||||
.eq(FormalSettlement::getSettlementType, RECEIVABLE)
|
||||
.eq(FormalSettlement::getApprovalStatus, APPROVED)
|
||||
.eq(FormalSettlement::getStatus, 1));
|
||||
List<FormalSettlement> merged = new ArrayList<>(relatedSettlements.size() + settlements.size());
|
||||
merged.addAll(relatedSettlements);
|
||||
merged.addAll(settlements);
|
||||
settlements = merged;
|
||||
}
|
||||
}
|
||||
// 候选结算单的累计认领金额一次聚合查询(避免逐单 N+1)
|
||||
Map<Long, BigDecimal> claimedMap = receiptClaimedAmounts(settlements.stream()
|
||||
.map(FormalSettlement::getId).toList());
|
||||
@@ -218,6 +239,14 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
.map(settlement -> candidateRow(settlement,
|
||||
claimedMap.getOrDefault(settlement.getId(), BigDecimal.ZERO)))
|
||||
.filter(row -> ((BigDecimal) row.get("remainingReceiptAmount")).compareTo(BigDecimal.ZERO) > 0)
|
||||
.sorted((left, right) -> {
|
||||
boolean leftRelated = relatedIds.contains(toLong(left.get("id")));
|
||||
boolean rightRelated = relatedIds.contains(toLong(right.get("id")));
|
||||
if (leftRelated == rightRelated) {
|
||||
return 0;
|
||||
}
|
||||
return leftRelated ? -1 : 1;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -234,6 +263,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getFormalSettlementId, formalSettlementId)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)
|
||||
.orderByDesc(ReceiptClaimSettlement::getCreateTime));
|
||||
if (relations.isEmpty()) {
|
||||
return List.of();
|
||||
@@ -315,19 +345,19 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
allocatedTotal = allocatedTotal.add(allocated);
|
||||
}
|
||||
|
||||
// 公告未认领金额(认领上限与认领完成判定基准,A301-2 口径)
|
||||
// 认领上限:公告未认领金额与收款金额取较小值(避免未认领金额异常偏大导致无法认领完成)
|
||||
if (flow.getUnclaimedAmount() == null) {
|
||||
throw new ServiceException("公告未认领金额缺失,请先手动同步金蝶收款流水");
|
||||
}
|
||||
BigDecimal unclaimedAmount = money(flow.getUnclaimedAmount());
|
||||
if (unclaimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("该流水公告未认领金额为0,无需认领");
|
||||
BigDecimal claimCeiling = claimCeiling(flow);
|
||||
if (claimCeiling.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("该流水可认领金额为0,无需认领");
|
||||
}
|
||||
// 流水认领前已认领金额
|
||||
BigDecimal previousFlowClaimed = flowClaimedAmount(flow.getId());
|
||||
if (previousFlowClaimed.add(allocatedTotal).compareTo(unclaimedAmount) > 0) {
|
||||
throw new ServiceException("累计认领金额不能超过公告未认领金额,剩余可认领 "
|
||||
+ unclaimedAmount.subtract(previousFlowClaimed).max(BigDecimal.ZERO));
|
||||
if (previousFlowClaimed.add(allocatedTotal).compareTo(claimCeiling) > 0) {
|
||||
throw new ServiceException("累计认领金额不能超过可认领上限,剩余可认领 "
|
||||
+ claimCeiling.subtract(previousFlowClaimed).max(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
// 当前用户部门
|
||||
@@ -380,6 +410,9 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
relation.setClaimedReceiptAmount(previousClaimed);
|
||||
// 本次分摊金额
|
||||
relation.setAllocatedReceiptAmount(allocated);
|
||||
// 有效分摊(与汇总/候选查询 status=1 口径一致)
|
||||
relation.setStatus(1);
|
||||
relation.setIsDeleted(0);
|
||||
claimSettlementMapper.insert(relation);
|
||||
}
|
||||
|
||||
@@ -390,8 +423,8 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
String fromStatus = normalizeClaimStatus(flow.getClaimStatus());
|
||||
// 认领后累计金额
|
||||
BigDecimal claimedAfter = previousFlowClaimed.add(allocatedTotal);
|
||||
// 认领后状态(对照公告未认领金额)
|
||||
String toStatus = amountClaimStatus(claimedAfter, unclaimedAmount);
|
||||
// 认领后状态(对照可认领上限)
|
||||
String toStatus = amountClaimStatus(claimedAfter, claimCeiling);
|
||||
// 更新流水已认领金额
|
||||
flow.setClaimedAmount(claimedAfter);
|
||||
// 更新流水认领状态
|
||||
@@ -499,9 +532,9 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
|
||||
// 复制公告数据到实体(不覆盖本地认领字段)
|
||||
copyAnnounceRow(entity, announce, detailSerialNo, receiptAmount);
|
||||
// 根据本地累计认领金额与最新公告未认领金额重新计算认领状态
|
||||
// 根据本地累计认领金额与可认领上限重新计算认领状态
|
||||
entity.setClaimStatus(amountClaimStatus(money(entity.getClaimedAmount()),
|
||||
entity.getUnclaimedAmount()));
|
||||
claimCeiling(entity)));
|
||||
|
||||
if (created) {
|
||||
toInsert.add(entity);
|
||||
@@ -621,6 +654,46 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本流水历史关联过的结算单 id(有效分摊)
|
||||
*/
|
||||
private Set<Long> relatedSettlementIds(Long flowId) {
|
||||
if (flowId == null) {
|
||||
return Set.of();
|
||||
}
|
||||
return claimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.select(ReceiptClaimSettlement::getFormalSettlementId)
|
||||
.eq(ReceiptClaimSettlement::getReceiptFlowId, flowId)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
|
||||
.map(ReceiptClaimSettlement::getFormalSettlementId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续认领预填:本流水历史关联且仍有剩余可认领金额的结算单
|
||||
*/
|
||||
private List<Map<String, Object>> relatedSettlementCandidates(Long flowId) {
|
||||
Set<Long> relatedIds = relatedSettlementIds(flowId);
|
||||
if (relatedIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<FormalSettlement> settlements = formalSettlementMapper.selectList(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.in(FormalSettlement::getId, relatedIds)
|
||||
.eq(FormalSettlement::getSettlementType, RECEIVABLE)
|
||||
.eq(FormalSettlement::getApprovalStatus, APPROVED)
|
||||
.eq(FormalSettlement::getStatus, 1));
|
||||
Map<Long, BigDecimal> claimedMap = receiptClaimedAmounts(settlements.stream()
|
||||
.map(FormalSettlement::getId).toList());
|
||||
return settlements.stream()
|
||||
.map(settlement -> candidateRow(settlement,
|
||||
claimedMap.getOrDefault(settlement.getId(), BigDecimal.ZERO)))
|
||||
.filter(row -> ((BigDecimal) row.get("remainingReceiptAmount")).compareTo(BigDecimal.ZERO) > 0)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量聚合结算单的累计有效认领金额:一次 IN 查询按结算单分组求和,避免逐单 N+1
|
||||
*/
|
||||
@@ -631,7 +704,8 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
return claimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.in(ReceiptClaimSettlement::getFormalSettlementId, ids)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)).stream()
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
|
||||
.collect(Collectors.groupingBy(ReceiptClaimSettlement::getFormalSettlementId,
|
||||
Collectors.reducing(BigDecimal.ZERO,
|
||||
item -> money(item.getAllocatedReceiptAmount()), BigDecimal::add)));
|
||||
@@ -640,12 +714,42 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
private BigDecimal flowClaimedAmount(Long flowId) {
|
||||
return claimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getReceiptFlowId, flowId)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)).stream()
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
|
||||
.map(ReceiptClaimSettlement::getAllocatedReceiptAmount)
|
||||
.map(this::money)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可认领上限:公告未认领金额与收款金额取较小正值。
|
||||
* 主口径仍为 A301-2 公告未认领金额;与收款金额取 min,避免未认领金额异常偏大导致无法进入「认领完成」。
|
||||
*/
|
||||
private BigDecimal claimCeiling(KingdeeReceiptFlow flow) {
|
||||
BigDecimal unclaimed = money(flow.getUnclaimedAmount());
|
||||
BigDecimal receipt = money(flow.getReceiptAmount());
|
||||
if (receipt.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return unclaimed;
|
||||
}
|
||||
if (unclaimed.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return unclaimed;
|
||||
}
|
||||
return unclaimed.min(receipt);
|
||||
}
|
||||
|
||||
private Long toLong(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Long longValue) {
|
||||
return longValue;
|
||||
}
|
||||
if (value instanceof Number number) {
|
||||
return number.longValue();
|
||||
}
|
||||
return Func.toLong(String.valueOf(value));
|
||||
}
|
||||
|
||||
private KingdeeReceiptFlow lockedFlow(Long flowId) {
|
||||
KingdeeReceiptFlow flow = baseMapper.selectOne(Wrappers.<KingdeeReceiptFlow>lambdaQuery()
|
||||
.eq(KingdeeReceiptFlow::getId, flowId)
|
||||
@@ -824,18 +928,20 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
flow.setClaimMe(announce.getClaimme());
|
||||
BigDecimal claimedTotal = flowClaimedAmount(flow.getId());
|
||||
flow.setClaimedAmount(claimedTotal);
|
||||
String claimStatus = amountClaimStatus(claimedTotal, flow.getUnclaimedAmount());
|
||||
BigDecimal claimCeiling = claimCeiling(flow);
|
||||
String claimStatus = amountClaimStatus(claimedTotal, claimCeiling);
|
||||
flow.setClaimStatus(claimStatus);
|
||||
updateById(flow);
|
||||
if (!CLAIMED.equals(claimStatus)) {
|
||||
return ReceiptClaimPushResult.fail("累计认领金额(" + claimedTotal + ")与公告未认领金额("
|
||||
+ money(flow.getUnclaimedAmount()) + ")不一致,不满足推送条件");
|
||||
return ReceiptClaimPushResult.fail("累计认领金额(" + claimedTotal + ")与可认领上限("
|
||||
+ claimCeiling + ")不一致,不满足推送条件");
|
||||
}
|
||||
// 分录取该流水全部有效分摊行(结算单id升序,跨认领主单归集)
|
||||
List<ReceiptClaimSettlement> allocations = claimSettlementMapper.selectList(
|
||||
Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getReceiptFlowId, flow.getId())
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0)
|
||||
.orderByAsc(ReceiptClaimSettlement::getFormalSettlementId));
|
||||
if (allocations.isEmpty()) {
|
||||
return ReceiptClaimPushResult.fail("该流水无有效认领分摊,无法推送");
|
||||
@@ -1075,13 +1181,13 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
|
||||
/**
|
||||
* 累计认领金额 → 流水认领状态:对照公告未认领金额判定(claimed 即「认领完成」,唯一可推送状态)
|
||||
* 累计认领金额 → 流水认领状态:对照可认领上限判定(claimed 即「认领完成」,唯一可推送状态)
|
||||
*/
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal unclaimedAmount) {
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal claimCeiling) {
|
||||
if (claimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return UNCLAIMED;
|
||||
}
|
||||
return claimedAmount.compareTo(money(unclaimedAmount)) >= 0 ? CLAIMED : PARTIAL;
|
||||
return claimedAmount.compareTo(money(claimCeiling)) >= 0 ? CLAIMED : PARTIAL;
|
||||
}
|
||||
|
||||
private String amountStatus(BigDecimal claimedAmount, BigDecimal settlementAmount) {
|
||||
|
||||
+2
-4
@@ -246,10 +246,8 @@ public class TemporaryCreditLimitServiceImpl extends BaseServiceImpl<TemporaryCr
|
||||
TransportBusinessSupport.validateRequired(temporaryCreditLimit.getProjectCode(), "项目编号不能为空");
|
||||
TransportBusinessSupport.validateRequired(temporaryCreditLimit.getUndertakeDeptName(), "承办部门不能为空");
|
||||
TransportBusinessSupport.validateRequired(temporaryCreditLimit.getValidUntil() == null ? null : temporaryCreditLimit.getValidUntil().toString(), "请选择申请有效期至");
|
||||
if (temporaryCreditLimit.getProjectFundLimit() == null || temporaryCreditLimit.getUsedFundLimit() == null || temporaryCreditLimit.getRemainingFundLimit() == null) {
|
||||
throw new ServiceException("项目额度数据异常,请重新选择项目");
|
||||
}
|
||||
if (temporaryCreditLimit.getRemainingFundLimit().compareTo(BigDecimal.ZERO) < 0) {
|
||||
if (temporaryCreditLimit.getRemainingFundLimit() != null
|
||||
&& temporaryCreditLimit.getRemainingFundLimit().compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new ServiceException("项目剩余额度不能小于0");
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -70,10 +70,15 @@ public class ReceiptFlowWrapper extends BaseEntityWrapper<KingdeeReceiptFlow, Re
|
||||
case "failed" -> "处理失败";
|
||||
default -> "未推送";
|
||||
});
|
||||
// 剩余可认领金额 = 公告未认领金额 − 累计认领金额(认领上限为公告口径)
|
||||
// 剩余可认领金额 = 可认领上限 − 累计认领金额(上限取公告未认领与收款金额较小值)
|
||||
BigDecimal unclaimedAmount = money(entity.getUnclaimedAmount());
|
||||
BigDecimal receiptAmount = money(entity.getReceiptAmount());
|
||||
BigDecimal claimCeiling = unclaimedAmount;
|
||||
if (receiptAmount.compareTo(BigDecimal.ZERO) > 0 && unclaimedAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
claimCeiling = unclaimedAmount.min(receiptAmount);
|
||||
}
|
||||
BigDecimal claimedAmount = money(entity.getClaimedAmount());
|
||||
vo.setRemainingAmount(unclaimedAmount.subtract(claimedAmount).max(BigDecimal.ZERO));
|
||||
vo.setRemainingAmount(claimCeiling.subtract(claimedAmount).max(BigDecimal.ZERO));
|
||||
vo.setCreateUserName(UserCache.getUserRealName(entity.getCreateUser()));
|
||||
vo.setUpdateUserName(UserCache.getUserRealName(entity.getUpdateUser()));
|
||||
return vo;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-- 用户表:新增 OA 登录名,支持用户管理后台维护
|
||||
-- 排序规则 utf8mb4_general_ci
|
||||
|
||||
ALTER TABLE `blade_user`
|
||||
ADD COLUMN `oa_login_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'OA登录名' AFTER `is_oa`;
|
||||
|
||||
-- 从用户部门关联表回填已有 OA 登录账号
|
||||
UPDATE `blade_user` bu
|
||||
INNER JOIN (
|
||||
SELECT `user_id`, MAX(`oa_login_id`) AS `oa_login_id`
|
||||
FROM `blade_user_dept`
|
||||
WHERE `is_deleted` = 0
|
||||
AND `oa_login_id` IS NOT NULL
|
||||
AND `oa_login_id` <> ''
|
||||
GROUP BY `user_id`
|
||||
) bud ON bud.`user_id` = bu.`id`
|
||||
SET bu.`oa_login_id` = bud.`oa_login_id`
|
||||
WHERE bu.`oa_login_id` IS NULL OR bu.`oa_login_id` = '';
|
||||
Reference in New Issue
Block a user