✨ 新增项目预付核销,并修复平台公司多选、常用线路组织过滤与登录 IP 连坐锁
结算附件支持项目预付核销并扣减可付款金额;加固项目平台公司多选字段扩容脚本;常用线路新增归属组织与列表筛选对齐;登录失败仅按账号锁定,取消 IP 级连坐。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+8
-7
@@ -84,16 +84,11 @@ public class BladeAuthorizationHandler extends AbstractAuthorizationHandler {
|
|||||||
if (bladeProperties.isProd() && isWeakPassword(request.getPassword())) {
|
if (bladeProperties.isProd() && isWeakPassword(request.getPassword())) {
|
||||||
return buildValidationFailure(ExceptionCode.INVALID_USER_PASSWORD);
|
return buildValidationFailure(ExceptionCode.INVALID_USER_PASSWORD);
|
||||||
}
|
}
|
||||||
// 判断账号是否锁定
|
// 仅按账号锁定;不做 IP 锁定,避免同出口/网关 IP 下一人失败牵连全部未登录用户
|
||||||
OAuth2Validation accountValidation = lockHandler.validateAccountLock(request.getTenantId(), request.getUsername());
|
OAuth2Validation accountValidation = lockHandler.validateAccountLock(request.getTenantId(), request.getUsername());
|
||||||
if (!accountValidation.isSuccess()) {
|
if (!accountValidation.isSuccess()) {
|
||||||
return accountValidation;
|
return accountValidation;
|
||||||
}
|
}
|
||||||
// 判断IP是否锁定
|
|
||||||
OAuth2Validation ipValidation = lockHandler.validateIpLock(request.getTenantId());
|
|
||||||
if (!ipValidation.isSuccess()) {
|
|
||||||
return ipValidation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return super.preValidation(request);
|
return super.preValidation(request);
|
||||||
}
|
}
|
||||||
@@ -105,7 +100,13 @@ public class BladeAuthorizationHandler extends AbstractAuthorizationHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void preFailure(OAuth2Request request, OAuth2Validation validation) {
|
public void preFailure(OAuth2Request request, OAuth2Validation validation) {
|
||||||
// 处理认证失败,增加错误次数
|
// 已因锁定失败时不再累加失败次数,避免其他账号重试时被误计并连带锁定
|
||||||
|
if (validation != null && !validation.isSuccess()
|
||||||
|
&& ExceptionCode.USER_TOO_MANY_FAILS.getCode() == validation.getCode()) {
|
||||||
|
log.error("用户:{},认证失败,失败原因:{}", request.getUsername(), validation.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 处理认证失败,增加错误次数(仅账号维度)
|
||||||
lockHandler.handleAuthFailure(request.getTenantId(), request.getUsername());
|
lockHandler.handleAuthFailure(request.getTenantId(), request.getUsername());
|
||||||
|
|
||||||
log.error("用户:{},认证失败,失败原因:{}", request.getUsername(), validation.getMessage());
|
log.error("用户:{},认证失败,失败原因:{}", request.getUsername(), validation.getMessage());
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class BladeLockHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理认证失败
|
* 处理认证失败
|
||||||
* 同时增加账号和IP错误次数
|
* 仅增加账号错误次数;不再累计 IP 失败次数,避免共享出口 IP 误伤其他用户。
|
||||||
*
|
*
|
||||||
* @param tenantId 租户id
|
* @param tenantId 租户id
|
||||||
* @param account 账号
|
* @param account 账号
|
||||||
@@ -102,7 +102,6 @@ public class BladeLockHandler {
|
|||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
authLockClient.addAccountFailCount(tenantId, account, userId, ip, userAgent);
|
authLockClient.addAccountFailCount(tenantId, account, userId, ip, userAgent);
|
||||||
authLockClient.addIpFailCount(tenantId, ip, userAgent);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("认证失败计数异常: {}", e.getMessage());
|
log.warn("认证失败计数异常: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -32,6 +32,8 @@ public class FormalSettlementSaveRequest implements Serializable {
|
|||||||
private String remark;
|
private String remark;
|
||||||
private List<SummaryFee> summaryFees;
|
private List<SummaryFee> summaryFees;
|
||||||
private List<Invoice> invoices;
|
private List<Invoice> invoices;
|
||||||
|
/** 项目预付核销 */
|
||||||
|
private List<SettlementProjectAdvanceWriteOffRequest> projectAdvanceWriteOffs;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class DetailAdjustment implements Serializable {
|
public static class DetailAdjustment implements Serializable {
|
||||||
|
|||||||
+3
@@ -76,6 +76,9 @@ public class PreSettlementSaveRequest implements Serializable {
|
|||||||
@Schema(description = "结算合计费用")
|
@Schema(description = "结算合计费用")
|
||||||
private List<SummaryFee> summaryFees;
|
private List<SummaryFee> summaryFees;
|
||||||
|
|
||||||
|
@Schema(description = "项目预付核销")
|
||||||
|
private List<SettlementProjectAdvanceWriteOffRequest> projectAdvanceWriteOffs;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Schema(description = "结算合计费用")
|
@Schema(description = "结算合计费用")
|
||||||
public static class SummaryFee implements Serializable {
|
public static class SummaryFee implements Serializable {
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单项目预付核销保存行
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "结算单项目预付核销保存行")
|
||||||
|
public class SettlementProjectAdvanceWriteOffRequest implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "项目预付付款申请ID")
|
||||||
|
private Long paymentApplicationId;
|
||||||
|
|
||||||
|
@Schema(description = "本次核销金额")
|
||||||
|
private BigDecimal writeOffAmount;
|
||||||
|
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springblade.core.tenant.mp.TenantEntity;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单项目预付核销实体类
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("blade_settlement_project_advance_write_off")
|
||||||
|
@Schema(description = "结算单项目预付核销")
|
||||||
|
public class SettlementProjectAdvanceWriteOff extends TenantEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "结算单类型:pre/formal")
|
||||||
|
private String settlementType;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "预结算/正式结算单ID")
|
||||||
|
private Long settlementId;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "项目预付付款申请ID")
|
||||||
|
private Long paymentApplicationId;
|
||||||
|
|
||||||
|
@Schema(description = "预付单号")
|
||||||
|
private String paymentNo;
|
||||||
|
|
||||||
|
@Schema(description = "项目预付金额")
|
||||||
|
private BigDecimal projectAdvanceAmount;
|
||||||
|
|
||||||
|
@Schema(description = "本次核销金额")
|
||||||
|
private BigDecimal writeOffAmount;
|
||||||
|
|
||||||
|
@Schema(description = "金蝶预付单号")
|
||||||
|
private String kingdeeBillNo;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@Schema(description = "可核销金额(动态计算)")
|
||||||
|
private BigDecimal availableWriteOffAmount;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@Schema(description = "预付单创建人姓名")
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@Schema(description = "预付单创建时间(展示用)")
|
||||||
|
private Date paymentCreateTime;
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -20,6 +20,7 @@ import org.springblade.transport.pojo.entity.FormalSettlementSource;
|
|||||||
import org.springblade.transport.pojo.entity.FormalSettlementPayment;
|
import org.springblade.transport.pojo.entity.FormalSettlementPayment;
|
||||||
import org.springblade.transport.pojo.entity.FormalSettlementSummaryFee;
|
import org.springblade.transport.pojo.entity.FormalSettlementSummaryFee;
|
||||||
import org.springblade.transport.pojo.entity.InvoiceApplicationInvoice;
|
import org.springblade.transport.pojo.entity.InvoiceApplicationInvoice;
|
||||||
|
import org.springblade.transport.pojo.entity.SettlementProjectAdvanceWriteOff;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
@@ -64,6 +65,8 @@ public class FormalSettlementVO extends FormalSettlement implements KingdeeUnloc
|
|||||||
@TableField(exist = false) private List<FormalSettlementSummaryFee> summaryFees;
|
@TableField(exist = false) private List<FormalSettlementSummaryFee> summaryFees;
|
||||||
@TableField(exist = false) private List<FormalSettlementPayment> payments;
|
@TableField(exist = false) private List<FormalSettlementPayment> payments;
|
||||||
@TableField(exist = false) private List<FormalSettlementInvoice> invoices;
|
@TableField(exist = false) private List<FormalSettlementInvoice> invoices;
|
||||||
|
@TableField(exist = false) @Schema(description = "项目预付核销")
|
||||||
|
private List<SettlementProjectAdvanceWriteOff> projectAdvanceWriteOffs;
|
||||||
@TableField(exist = false) @Schema(description = "开票申请经金蝶回写的发票结果(含文件地址,只读展示)")
|
@TableField(exist = false) @Schema(description = "开票申请经金蝶回写的发票结果(含文件地址,只读展示)")
|
||||||
private List<InvoiceApplicationInvoice> issuedInvoices;
|
private List<InvoiceApplicationInvoice> issuedInvoices;
|
||||||
@TableField(exist = false) private List<PaymentApplicationVO> paymentApplications;
|
@TableField(exist = false) private List<PaymentApplicationVO> paymentApplications;
|
||||||
|
|||||||
+5
@@ -35,6 +35,7 @@ import org.springblade.transport.pojo.entity.PreSettlementChangeRecord;
|
|||||||
import org.springblade.transport.pojo.entity.PreSettlementDetail;
|
import org.springblade.transport.pojo.entity.PreSettlementDetail;
|
||||||
import org.springblade.transport.pojo.entity.PreSettlementDetailFee;
|
import org.springblade.transport.pojo.entity.PreSettlementDetailFee;
|
||||||
import org.springblade.transport.pojo.entity.PreSettlementSummaryFee;
|
import org.springblade.transport.pojo.entity.PreSettlementSummaryFee;
|
||||||
|
import org.springblade.transport.pojo.entity.SettlementProjectAdvanceWriteOff;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
@@ -100,6 +101,10 @@ public class PreSettlementVO extends PreSettlement {
|
|||||||
@Schema(description = "预付信息")
|
@Schema(description = "预付信息")
|
||||||
private List<PreSettlementAdvance> advances;
|
private List<PreSettlementAdvance> advances;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@Schema(description = "项目预付核销")
|
||||||
|
private List<SettlementProjectAdvanceWriteOff> projectAdvanceWriteOffs;
|
||||||
|
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
@Schema(description = "变更记录")
|
@Schema(description = "变更记录")
|
||||||
private List<PreSettlementChangeRecord> changeRecords;
|
private List<PreSettlementChangeRecord> changeRecords;
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选项目预付核销选项
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "可选项目预付核销选项")
|
||||||
|
public class ProjectAdvanceWriteOffOptionVO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "付款申请ID")
|
||||||
|
private Long paymentApplicationId;
|
||||||
|
|
||||||
|
@Schema(description = "预付单号")
|
||||||
|
private String paymentNo;
|
||||||
|
|
||||||
|
@Schema(description = "项目预付金额")
|
||||||
|
private BigDecimal projectAdvanceAmount;
|
||||||
|
|
||||||
|
@Schema(description = "可核销金额")
|
||||||
|
private BigDecimal availableWriteOffAmount;
|
||||||
|
|
||||||
|
@Schema(description = "金蝶预付单号")
|
||||||
|
private String kingdeeBillNo;
|
||||||
|
|
||||||
|
@Schema(description = "创建人姓名")
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
}
|
||||||
+12
@@ -31,9 +31,21 @@ package org.springblade.transport.constant;
|
|||||||
*/
|
*/
|
||||||
public final class TransportBusiConstant {
|
public final class TransportBusiConstant {
|
||||||
|
|
||||||
|
/** 付款类型:项目预付。 */
|
||||||
|
public static final String PAYMENT_TYPE_PROJECT_ADVANCE = "project_advance";
|
||||||
|
|
||||||
|
/** 付款类型:进度预付。 */
|
||||||
|
public static final String PAYMENT_TYPE_PROGRESS_ADVANCE = "progress_advance";
|
||||||
|
|
||||||
/** 付款类型:结算付款(关联正式结算单的正式付款)。 */
|
/** 付款类型:结算付款(关联正式结算单的正式付款)。 */
|
||||||
public static final String PAYMENT_TYPE_SETTLEMENT = "settlement_payment";
|
public static final String PAYMENT_TYPE_SETTLEMENT = "settlement_payment";
|
||||||
|
|
||||||
|
/** 结算单类型:预结算。 */
|
||||||
|
public static final String SETTLEMENT_BILL_PRE = "pre";
|
||||||
|
|
||||||
|
/** 结算单类型:正式结算。 */
|
||||||
|
public static final String SETTLEMENT_BILL_FORMAL = "formal";
|
||||||
|
|
||||||
/** 审批状态:草稿(允许编辑、删除)。 */
|
/** 审批状态:草稿(允许编辑、删除)。 */
|
||||||
public static final String DRAFT = "draft";
|
public static final String DRAFT = "draft";
|
||||||
|
|
||||||
|
|||||||
+12
@@ -34,6 +34,7 @@ import org.springblade.transport.pojo.dto.PaymentApplicationSaveRequest;
|
|||||||
import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest;
|
import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest;
|
||||||
import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO;
|
import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO;
|
||||||
import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
||||||
|
import org.springblade.transport.pojo.vo.ProjectAdvanceWriteOffOptionVO;
|
||||||
import org.springblade.transport.service.IPaymentApplicationService;
|
import org.springblade.transport.service.IPaymentApplicationService;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -67,6 +68,17 @@ public class PaymentApplicationController extends BladeController {
|
|||||||
@RequestParam Long referenceId, @RequestParam(required = false) Long excludeId) {
|
@RequestParam Long referenceId, @RequestParam(required = false) Long excludeId) {
|
||||||
return R.data(paymentApplicationService.referenceAmount(paymentType, referenceId, excludeId));
|
return R.data(paymentApplicationService.referenceAmount(paymentType, referenceId, excludeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/project-advance-write-off-options")
|
||||||
|
@ApiOperationSupport(order = 3)
|
||||||
|
@Operation(summary = "按合同查询可选项目预付核销列表")
|
||||||
|
public R<List<ProjectAdvanceWriteOffOptionVO>> projectAdvanceWriteOffOptions(
|
||||||
|
@RequestParam Long contractId,
|
||||||
|
@RequestParam(required = false) String excludeSettlementType,
|
||||||
|
@RequestParam(required = false) Long excludeSettlementId) {
|
||||||
|
return R.data(paymentApplicationService.projectAdvanceWriteOffOptions(
|
||||||
|
contractId, excludeSettlementType, excludeSettlementId));
|
||||||
|
}
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
@ApiOperationSupport(order = 4)
|
@ApiOperationSupport(order = 4)
|
||||||
@Operation(summary = "保存付款申请")
|
@Operation(summary = "保存付款申请")
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springblade.transport.pojo.entity.SettlementProjectAdvanceWriteOff;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单项目预付核销 Mapper
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SettlementProjectAdvanceWriteOffMapper extends BaseMapper<SettlementProjectAdvanceWriteOff> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计项目预付已核销金额(排除作废结算单,可排除当前结算单)。
|
||||||
|
*/
|
||||||
|
BigDecimal sumWriteOffByPaymentApplication(@Param("paymentApplicationId") Long paymentApplicationId,
|
||||||
|
@Param("excludeSettlementType") String excludeSettlementType,
|
||||||
|
@Param("excludeSettlementId") Long excludeSettlementId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计结算单核销合计。
|
||||||
|
*/
|
||||||
|
BigDecimal sumWriteOffBySettlement(@Param("settlementType") String settlementType,
|
||||||
|
@Param("settlementId") Long settlementId);
|
||||||
|
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="org.springblade.transport.mapper.SettlementProjectAdvanceWriteOffMapper">
|
||||||
|
|
||||||
|
<select id="sumWriteOffByPaymentApplication" resultType="java.math.BigDecimal">
|
||||||
|
SELECT COALESCE(SUM(w.write_off_amount), 0)
|
||||||
|
FROM blade_settlement_project_advance_write_off w
|
||||||
|
LEFT JOIN blade_pre_settlement p
|
||||||
|
ON w.settlement_type = 'pre'
|
||||||
|
AND w.settlement_id = p.id
|
||||||
|
AND p.is_deleted = 0
|
||||||
|
LEFT JOIN blade_formal_settlement f
|
||||||
|
ON w.settlement_type = 'formal'
|
||||||
|
AND w.settlement_id = f.id
|
||||||
|
AND f.is_deleted = 0
|
||||||
|
WHERE w.is_deleted = 0
|
||||||
|
AND w.payment_application_id = #{paymentApplicationId}
|
||||||
|
AND (
|
||||||
|
(w.settlement_type = 'pre' AND p.id IS NOT NULL AND IFNULL(p.approval_status, '') <> 'voided')
|
||||||
|
OR (w.settlement_type = 'formal' AND f.id IS NOT NULL AND IFNULL(f.approval_status, '') <> 'voided')
|
||||||
|
)
|
||||||
|
<if test="excludeSettlementType != null and excludeSettlementId != null">
|
||||||
|
AND NOT (w.settlement_type = #{excludeSettlementType} AND w.settlement_id = #{excludeSettlementId})
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="sumWriteOffBySettlement" resultType="java.math.BigDecimal">
|
||||||
|
SELECT COALESCE(SUM(write_off_amount), 0)
|
||||||
|
FROM blade_settlement_project_advance_write_off
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND settlement_type = #{settlementType}
|
||||||
|
AND settlement_id = #{settlementId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
+6
@@ -28,6 +28,7 @@ import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest;
|
|||||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||||
import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO;
|
import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO;
|
||||||
import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
||||||
|
import org.springblade.transport.pojo.vo.ProjectAdvanceWriteOffOptionVO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -36,6 +37,11 @@ public interface IPaymentApplicationService extends BaseService<PaymentApplicati
|
|||||||
IPage<PaymentApplicationVO> selectPage(IPage<PaymentApplication> page, PaymentApplicationVO query);
|
IPage<PaymentApplicationVO> selectPage(IPage<PaymentApplication> page, PaymentApplicationVO query);
|
||||||
PaymentApplicationVO detail(Long id);
|
PaymentApplicationVO detail(Long id);
|
||||||
PaymentApplicationReferenceAmountVO referenceAmount(String paymentType, Long referenceId, Long excludeId);
|
PaymentApplicationReferenceAmountVO referenceAmount(String paymentType, Long referenceId, Long excludeId);
|
||||||
|
|
||||||
|
/** 按合同查询可选项目预付核销列表。 */
|
||||||
|
List<ProjectAdvanceWriteOffOptionVO> projectAdvanceWriteOffOptions(Long contractId,
|
||||||
|
String excludeSettlementType,
|
||||||
|
Long excludeSettlementId);
|
||||||
Long saveDraft(PaymentApplicationSaveRequest request);
|
Long saveDraft(PaymentApplicationSaveRequest request);
|
||||||
void removeDraft(Long id);
|
void removeDraft(Long id);
|
||||||
void submit(PaymentApplicationStatusRequest request);
|
void submit(PaymentApplicationStatusRequest request);
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.service;
|
||||||
|
|
||||||
|
import org.springblade.transport.pojo.dto.SettlementProjectAdvanceWriteOffRequest;
|
||||||
|
import org.springblade.transport.pojo.entity.SettlementProjectAdvanceWriteOff;
|
||||||
|
import org.springblade.transport.pojo.vo.ProjectAdvanceWriteOffOptionVO;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单项目预付核销服务
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public interface ISettlementProjectAdvanceWriteOffService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按合同查询可选项目预付(审批通过且仍有可核销余额)。
|
||||||
|
*/
|
||||||
|
List<ProjectAdvanceWriteOffOptionVO> listOptions(Long contractId, String excludeSettlementType,
|
||||||
|
Long excludeSettlementId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询结算单已保存的核销明细(含动态可核销金额)。
|
||||||
|
*/
|
||||||
|
List<SettlementProjectAdvanceWriteOff> listBySettlement(String settlementType, Long settlementId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单核销金额合计。
|
||||||
|
*/
|
||||||
|
BigDecimal sumBySettlement(String settlementType, Long settlementId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 覆盖保存核销明细,并校验不超过可核销金额与结算单金额。
|
||||||
|
*/
|
||||||
|
void replaceWriteOffs(String settlementType, Long settlementId, Long contractId,
|
||||||
|
BigDecimal settlementAmount, List<SettlementProjectAdvanceWriteOffRequest> writeOffs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理清理结算单核销明细(草稿删除时)。
|
||||||
|
*/
|
||||||
|
void deleteBySettlement(String settlementType, Long settlementId);
|
||||||
|
|
||||||
|
}
|
||||||
+23
-2
@@ -30,6 +30,7 @@ import org.springblade.core.log.exception.ServiceException;
|
|||||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||||
import org.springblade.core.tool.utils.BeanUtil;
|
import org.springblade.core.tool.utils.BeanUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.system.cache.SysCache;
|
||||||
import org.springblade.system.cache.UserCache;
|
import org.springblade.system.cache.UserCache;
|
||||||
import org.springblade.system.pojo.entity.Dept;
|
import org.springblade.system.pojo.entity.Dept;
|
||||||
import org.springblade.transport.excel.CommonRouteExportExcel;
|
import org.springblade.transport.excel.CommonRouteExportExcel;
|
||||||
@@ -217,12 +218,32 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
|
|||||||
commonRoute.setArrivalPhone(TransportBusinessSupport.trimToNull(commonRoute.getArrivalPhone()));
|
commonRoute.setArrivalPhone(TransportBusinessSupport.trimToNull(commonRoute.getArrivalPhone()));
|
||||||
commonRoute.setDeptName(TransportBusinessSupport.trimToNull(commonRoute.getDeptName()));
|
commonRoute.setDeptName(TransportBusinessSupport.trimToNull(commonRoute.getDeptName()));
|
||||||
commonRoute.setRemark(TransportBusinessSupport.trimToNull(commonRoute.getRemark()));
|
commonRoute.setRemark(TransportBusinessSupport.trimToNull(commonRoute.getRemark()));
|
||||||
if (Func.isEmpty(commonRoute.getDeptId())) {
|
// 仅新增时绑定组织;编辑沿用原组织(submit 中已从旧记录写回)。
|
||||||
|
if (Func.isEmpty(commonRoute.getId())) {
|
||||||
|
bindDeptOnCreate(commonRoute);
|
||||||
|
}
|
||||||
|
if (commonRoute.getStatus() == null) { commonRoute.setStatus(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增绑定所属组织:普通用户始终落当前组织;超管可按入参组织保存,未传时回落当前组织。
|
||||||
|
*/
|
||||||
|
private void bindDeptOnCreate(CommonRoute commonRoute) {
|
||||||
|
if (commonRoute == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean administrator = TransportBusinessSupport.isAdministrator();
|
||||||
|
if (!administrator || Func.isEmpty(commonRoute.getDeptId()) || commonRoute.getDeptId() <= 0) {
|
||||||
Dept dept = TransportBusinessSupport.currentDept("常用线路");
|
Dept dept = TransportBusinessSupport.currentDept("常用线路");
|
||||||
commonRoute.setDeptId(dept.getId());
|
commonRoute.setDeptId(dept.getId());
|
||||||
commonRoute.setDeptName(dept.getDeptName());
|
commonRoute.setDeptName(dept.getDeptName());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (commonRoute.getStatus() == null) { commonRoute.setStatus(1); }
|
Dept dept = SysCache.getDept(commonRoute.getDeptId());
|
||||||
|
if (Func.isEmpty(dept)) {
|
||||||
|
throw new ServiceException("所属组织不存在或已删除");
|
||||||
|
}
|
||||||
|
commonRoute.setDeptName(TransportBusinessSupport.trimToNull(dept.getDeptName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate(CommonRoute commonRoute) {
|
private void validate(CommonRoute commonRoute) {
|
||||||
|
|||||||
+11
@@ -88,6 +88,7 @@ import org.springblade.transport.service.IInvoiceReceiptService;
|
|||||||
import org.springblade.transport.service.IKingdeeBillLockService;
|
import org.springblade.transport.service.IKingdeeBillLockService;
|
||||||
import org.springblade.transport.service.IContractManageService;
|
import org.springblade.transport.service.IContractManageService;
|
||||||
import org.springblade.transport.service.IPreSettlementService;
|
import org.springblade.transport.service.IPreSettlementService;
|
||||||
|
import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.service.KingdeeSyncContext;
|
import org.springblade.transport.service.KingdeeSyncContext;
|
||||||
import org.springblade.transport.support.KingdeeApBillSyncSupport;
|
import org.springblade.transport.support.KingdeeApBillSyncSupport;
|
||||||
@@ -170,6 +171,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
|||||||
private final SettlementAdjustmentMapper settlementAdjustmentMapper;
|
private final SettlementAdjustmentMapper settlementAdjustmentMapper;
|
||||||
private final IContractManageService contractManageService;
|
private final IContractManageService contractManageService;
|
||||||
private final IPreSettlementService preSettlementService;
|
private final IPreSettlementService preSettlementService;
|
||||||
|
private final ISettlementProjectAdvanceWriteOffService projectAdvanceWriteOffService;
|
||||||
private final IWaybillService waybillService;
|
private final IWaybillService waybillService;
|
||||||
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
|
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
|
||||||
private final KingdeeArBillSyncSupport kingdeeArBillSyncSupport;
|
private final KingdeeArBillSyncSupport kingdeeArBillSyncSupport;
|
||||||
@@ -318,6 +320,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
|||||||
.eq(FormalSettlementPayment::getFormalSettlementId, id).orderByDesc(FormalSettlementPayment::getCreateTime)));
|
.eq(FormalSettlementPayment::getFormalSettlementId, id).orderByDesc(FormalSettlementPayment::getCreateTime)));
|
||||||
vo.setInvoices(listInvoices(settlement));
|
vo.setInvoices(listInvoices(settlement));
|
||||||
vo.setIssuedInvoices(listIssuedInvoices(id));
|
vo.setIssuedInvoices(listIssuedInvoices(id));
|
||||||
|
vo.setProjectAdvanceWriteOffs(projectAdvanceWriteOffService.listBySettlement(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_FORMAL, id));
|
||||||
Set<Long> relationApplicationIds = paymentApplicationSettlementMapper.selectList(
|
Set<Long> relationApplicationIds = paymentApplicationSettlementMapper.selectList(
|
||||||
Wrappers.<PaymentApplicationSettlement>lambdaQuery()
|
Wrappers.<PaymentApplicationSettlement>lambdaQuery()
|
||||||
.select(PaymentApplicationSettlement::getPaymentApplicationId)
|
.select(PaymentApplicationSettlement::getPaymentApplicationId)
|
||||||
@@ -541,6 +545,12 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
|||||||
applySummaryRequest(settlement.getId(), request.getSummaryFees());
|
applySummaryRequest(settlement.getId(), request.getSummaryFees());
|
||||||
refreshSettlementAmount(settlement);
|
refreshSettlementAmount(settlement);
|
||||||
rebuildInvoices(settlement, request.getInvoices());
|
rebuildInvoices(settlement, request.getInvoices());
|
||||||
|
projectAdvanceWriteOffService.replaceWriteOffs(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_FORMAL,
|
||||||
|
settlement.getId(),
|
||||||
|
settlement.getContractId(),
|
||||||
|
settlement.getSettlementAmount(),
|
||||||
|
request.getProjectAdvanceWriteOffs());
|
||||||
if (!creating && !restoring) {
|
if (!creating && !restoring) {
|
||||||
saveChange(settlement.getId(), "结算单基本信息", null, "调整",
|
saveChange(settlement.getId(), "结算单基本信息", null, "调整",
|
||||||
"保存正式结算单" + settlement.getFormalSettlementNo(), request.getRemark());
|
"保存正式结算单" + settlement.getFormalSettlementNo(), request.getRemark());
|
||||||
@@ -562,6 +572,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
|||||||
.eq(FormalSettlementSummaryFee::getFormalSettlementId, id));
|
.eq(FormalSettlementSummaryFee::getFormalSettlementId, id));
|
||||||
invoiceMapper.delete(Wrappers.<FormalSettlementInvoice>lambdaQuery()
|
invoiceMapper.delete(Wrappers.<FormalSettlementInvoice>lambdaQuery()
|
||||||
.eq(FormalSettlementInvoice::getFormalSettlementId, id));
|
.eq(FormalSettlementInvoice::getFormalSettlementId, id));
|
||||||
|
projectAdvanceWriteOffService.deleteBySettlement(TransportBusiConstant.SETTLEMENT_BILL_FORMAL, id);
|
||||||
changeRecordMapper.delete(Wrappers.<FormalSettlementChangeRecord>lambdaQuery()
|
changeRecordMapper.delete(Wrappers.<FormalSettlementChangeRecord>lambdaQuery()
|
||||||
.eq(FormalSettlementChangeRecord::getFormalSettlementId, id));
|
.eq(FormalSettlementChangeRecord::getFormalSettlementId, id));
|
||||||
removeById(id);
|
removeById(id);
|
||||||
|
|||||||
+40
-9
@@ -98,7 +98,9 @@ import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
|||||||
import org.springblade.transport.service.IFormalSettlementService;
|
import org.springblade.transport.service.IFormalSettlementService;
|
||||||
import org.springblade.transport.service.IKingdeeBillLockService;
|
import org.springblade.transport.service.IKingdeeBillLockService;
|
||||||
import org.springblade.transport.service.IPaymentApplicationService;
|
import org.springblade.transport.service.IPaymentApplicationService;
|
||||||
|
import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService;
|
||||||
import org.springblade.transport.service.KingdeeSyncContext;
|
import org.springblade.transport.service.KingdeeSyncContext;
|
||||||
|
import org.springblade.transport.pojo.vo.ProjectAdvanceWriteOffOptionVO;
|
||||||
import org.springblade.transport.support.KingdeeApBillSyncSupport;
|
import org.springblade.transport.support.KingdeeApBillSyncSupport;
|
||||||
import org.springblade.transport.support.KingdeeAttachmentSyncSupport;
|
import org.springblade.transport.support.KingdeeAttachmentSyncSupport;
|
||||||
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
||||||
@@ -159,6 +161,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
private final BillLedgerUsageMapper billLedgerUsageMapper;
|
private final BillLedgerUsageMapper billLedgerUsageMapper;
|
||||||
private final TemporaryCreditLimitMapper temporaryCreditLimitMapper;
|
private final TemporaryCreditLimitMapper temporaryCreditLimitMapper;
|
||||||
private final IFormalSettlementService formalSettlementService;
|
private final IFormalSettlementService formalSettlementService;
|
||||||
|
private final ISettlementProjectAdvanceWriteOffService projectAdvanceWriteOffService;
|
||||||
private final IKingdeeBillLockService kingdeeBillLockService;
|
private final IKingdeeBillLockService kingdeeBillLockService;
|
||||||
private final IBfiService bfiService;
|
private final IBfiService bfiService;
|
||||||
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
|
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
|
||||||
@@ -301,13 +304,24 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
BigDecimal cumulativeAppliedAmount = preSettlementReference
|
BigDecimal cumulativeAppliedAmount = preSettlementReference
|
||||||
? cumulativePreSettlementApplied(referenceId, excludeId)
|
? cumulativePreSettlementApplied(referenceId, excludeId)
|
||||||
: cumulativeFormalSettlementApplied(referenceId, excludeId);
|
: cumulativeFormalSettlementApplied(referenceId, excludeId);
|
||||||
|
BigDecimal writeOffAmount = preSettlementReference
|
||||||
|
? projectAdvanceWriteOffService.sumBySettlement(TransportBusiConstant.SETTLEMENT_BILL_PRE, referenceId)
|
||||||
|
: projectAdvanceWriteOffService.sumBySettlement(TransportBusiConstant.SETTLEMENT_BILL_FORMAL, referenceId);
|
||||||
PaymentApplicationReferenceAmountVO amount = new PaymentApplicationReferenceAmountVO();
|
PaymentApplicationReferenceAmountVO amount = new PaymentApplicationReferenceAmountVO();
|
||||||
amount.setSettlementAmount(settlementAmount);
|
amount.setSettlementAmount(settlementAmount);
|
||||||
amount.setCumulativeAppliedAmount(cumulativeAppliedAmount);
|
amount.setCumulativeAppliedAmount(cumulativeAppliedAmount.add(writeOffAmount));
|
||||||
amount.setPayableAmount(settlementAmount.subtract(cumulativeAppliedAmount));
|
amount.setPayableAmount(settlementAmount.subtract(cumulativeAppliedAmount).subtract(writeOffAmount)
|
||||||
|
.max(BigDecimal.ZERO));
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProjectAdvanceWriteOffOptionVO> projectAdvanceWriteOffOptions(Long contractId,
|
||||||
|
String excludeSettlementType,
|
||||||
|
Long excludeSettlementId) {
|
||||||
|
return projectAdvanceWriteOffService.listOptions(contractId, excludeSettlementType, excludeSettlementId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Long saveDraft(PaymentApplicationSaveRequest request) {
|
public Long saveDraft(PaymentApplicationSaveRequest request) {
|
||||||
@@ -1741,12 +1755,12 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
if (source == null) throw new ServiceException("正式结算单不存在");
|
if (source == null) throw new ServiceException("正式结算单不存在");
|
||||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) 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.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(money(source.getSettlementAmount()).subtract(cumulativeFormalSettlementApplied(source.getId(), entity.getId()))); entity.setBillType("正式结算单");
|
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) {
|
} else if (request.getPreSettlementId() != null) {
|
||||||
PreSettlement source = preSettlementMapper.selectById(request.getPreSettlementId());
|
PreSettlement source = preSettlementMapper.selectById(request.getPreSettlementId());
|
||||||
if (source == null) throw new ServiceException("预结算单不存在");
|
if (source == null) throw new ServiceException("预结算单不存在");
|
||||||
if (!TransportBusiConstant.APPROVED.equals(source.getApprovalStatus())) 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(money(source.getSettlementAmount()).subtract(cumulativePreSettlementApplied(source.getId(), entity.getId()))); entity.setBillType("预结算单");
|
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("预结算单");
|
||||||
} else {
|
} 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());
|
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());
|
||||||
}
|
}
|
||||||
@@ -1782,7 +1796,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
BigDecimal settlementAmount = settlements.stream().map(FormalSettlement::getSettlementAmount)
|
BigDecimal settlementAmount = settlements.stream().map(FormalSettlement::getSettlementAmount)
|
||||||
.map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
|
.map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
BigDecimal payableAmount = settlements.stream()
|
BigDecimal payableAmount = settlements.stream()
|
||||||
.map(item -> money(item.getSettlementAmount()).subtract(cumulativeFormalSettlementApplied(item.getId(), entity.getId())).max(BigDecimal.ZERO))
|
.map(item -> payableOfFormalSettlement(item.getId(), item.getSettlementAmount(), entity.getId()))
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
if (money(entity.getAppliedAmount()).compareTo(payableAmount) > 0) throw new ServiceException("申请付款金额不能超过所选结算单可付款金额合计");
|
if (money(entity.getAppliedAmount()).compareTo(payableAmount) > 0) throw new ServiceException("申请付款金额不能超过所选结算单可付款金额合计");
|
||||||
FormalSettlement first = settlements.get(0);
|
FormalSettlement first = settlements.get(0);
|
||||||
@@ -1796,15 +1810,14 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
if (settlements.isEmpty()) return;
|
if (settlements.isEmpty()) return;
|
||||||
settlementRelationMapper.delete(Wrappers.<PaymentApplicationSettlement>lambdaQuery()
|
settlementRelationMapper.delete(Wrappers.<PaymentApplicationSettlement>lambdaQuery()
|
||||||
.eq(PaymentApplicationSettlement::getPaymentApplicationId, entity.getId()));
|
.eq(PaymentApplicationSettlement::getPaymentApplicationId, entity.getId()));
|
||||||
BigDecimal totalBase = settlements.stream().map(item -> money(item.getSettlementAmount()).subtract(
|
BigDecimal totalBase = settlements.stream()
|
||||||
cumulativeFormalSettlementApplied(item.getId(), entity.getId())).max(BigDecimal.ZERO))
|
.map(item -> payableOfFormalSettlement(item.getId(), item.getSettlementAmount(), entity.getId()))
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
BigDecimal remainingApplied = money(entity.getAppliedAmount());
|
BigDecimal remainingApplied = money(entity.getAppliedAmount());
|
||||||
BigDecimal remainingPaid = money(entity.getPaidAmount());
|
BigDecimal remainingPaid = money(entity.getPaidAmount());
|
||||||
for (int index = 0; index < settlements.size(); index++) {
|
for (int index = 0; index < settlements.size(); index++) {
|
||||||
FormalSettlement settlement = settlements.get(index);
|
FormalSettlement settlement = settlements.get(index);
|
||||||
BigDecimal base = money(settlement.getSettlementAmount()).subtract(
|
BigDecimal base = payableOfFormalSettlement(settlement.getId(), settlement.getSettlementAmount(), entity.getId());
|
||||||
cumulativeFormalSettlementApplied(settlement.getId(), entity.getId())).max(BigDecimal.ZERO);
|
|
||||||
BigDecimal applied = index == settlements.size() - 1 ? remainingApplied
|
BigDecimal applied = index == settlements.size() - 1 ? remainingApplied
|
||||||
: money(entity.getAppliedAmount()).multiply(base).divide(totalBase, 2, RoundingMode.DOWN);
|
: money(entity.getAppliedAmount()).multiply(base).divide(totalBase, 2, RoundingMode.DOWN);
|
||||||
BigDecimal paid = index == settlements.size() - 1 ? remainingPaid
|
BigDecimal paid = index == settlements.size() - 1 ? remainingPaid
|
||||||
@@ -1881,6 +1894,24 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
.ne(excludeId != null, PaymentApplication::getId, excludeId));
|
.ne(excludeId != null, PaymentApplication::getId, excludeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 正式结算可付款 = 结算金额 − 已申请付款 − 项目预付核销 */
|
||||||
|
private BigDecimal payableOfFormalSettlement(Long settlementId, BigDecimal settlementAmount, Long excludeId) {
|
||||||
|
return money(settlementAmount)
|
||||||
|
.subtract(cumulativeFormalSettlementApplied(settlementId, excludeId))
|
||||||
|
.subtract(projectAdvanceWriteOffService.sumBySettlement(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_FORMAL, settlementId))
|
||||||
|
.max(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预结算可付款 = 结算金额 − 已申请付款 − 项目预付核销 */
|
||||||
|
private BigDecimal payableOfPreSettlement(Long preSettlementId, BigDecimal settlementAmount, Long excludeId) {
|
||||||
|
return money(settlementAmount)
|
||||||
|
.subtract(cumulativePreSettlementApplied(preSettlementId, excludeId))
|
||||||
|
.subtract(projectAdvanceWriteOffService.sumBySettlement(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_PRE, preSettlementId))
|
||||||
|
.max(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
private void fillBillLedger(PaymentApplication entity, PaymentApplicationSaveRequest request) {
|
private void fillBillLedger(PaymentApplication entity, PaymentApplicationSaveRequest request) {
|
||||||
if (!isBillPayment(request.getPaymentMethod())) {
|
if (!isBillPayment(request.getPaymentMethod())) {
|
||||||
entity.setBillLedgerId(null);
|
entity.setBillLedgerId(null);
|
||||||
|
|||||||
+12
@@ -36,6 +36,7 @@ import org.springblade.core.secure.utils.AuthUtil;
|
|||||||
import org.springblade.core.tool.jackson.JsonUtil;
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
import org.springblade.core.tool.utils.BeanUtil;
|
import org.springblade.core.tool.utils.BeanUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.transport.constant.TransportBusiConstant;
|
||||||
import org.springblade.transport.mapper.PreSettlementAdvanceMapper;
|
import org.springblade.transport.mapper.PreSettlementAdvanceMapper;
|
||||||
import org.springblade.transport.mapper.PreSettlementChangeRecordMapper;
|
import org.springblade.transport.mapper.PreSettlementChangeRecordMapper;
|
||||||
import org.springblade.transport.mapper.PreSettlementDetailFeeMapper;
|
import org.springblade.transport.mapper.PreSettlementDetailFeeMapper;
|
||||||
@@ -67,6 +68,7 @@ import org.springblade.transport.service.IContractManageService;
|
|||||||
import org.springblade.transport.service.ICustomerArchiveService;
|
import org.springblade.transport.service.ICustomerArchiveService;
|
||||||
import org.springblade.transport.service.IPreSettlementService;
|
import org.springblade.transport.service.IPreSettlementService;
|
||||||
import org.springblade.transport.service.IProjectApplyService;
|
import org.springblade.transport.service.IProjectApplyService;
|
||||||
|
import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.wrapper.PreSettlementWrapper;
|
import org.springblade.transport.wrapper.PreSettlementWrapper;
|
||||||
import org.springblade.system.cache.UserCache;
|
import org.springblade.system.cache.UserCache;
|
||||||
@@ -124,6 +126,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
private final ICustomerArchiveService customerArchiveService;
|
private final ICustomerArchiveService customerArchiveService;
|
||||||
private final IWaybillService waybillService;
|
private final IWaybillService waybillService;
|
||||||
private final ISysClient sysClient;
|
private final ISysClient sysClient;
|
||||||
|
private final ISettlementProjectAdvanceWriteOffService projectAdvanceWriteOffService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<PreSettlementVO> selectPage(IPage<PreSettlement> page, PreSettlementVO query) {
|
public IPage<PreSettlementVO> selectPage(IPage<PreSettlement> page, PreSettlementVO query) {
|
||||||
@@ -198,6 +201,8 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
List<PreSettlementAdvance> advances = listAdvances(id);
|
List<PreSettlementAdvance> advances = listAdvances(id);
|
||||||
advances.forEach(advance -> advance.setCreateUserName(UserCache.getUserRealName(advance.getCreateUser())));
|
advances.forEach(advance -> advance.setCreateUserName(UserCache.getUserRealName(advance.getCreateUser())));
|
||||||
vo.setAdvances(advances);
|
vo.setAdvances(advances);
|
||||||
|
vo.setProjectAdvanceWriteOffs(projectAdvanceWriteOffService.listBySettlement(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_PRE, id));
|
||||||
vo.setChangeRecords(changeRecordMapper.selectList(Wrappers.<PreSettlementChangeRecord>lambdaQuery()
|
vo.setChangeRecords(changeRecordMapper.selectList(Wrappers.<PreSettlementChangeRecord>lambdaQuery()
|
||||||
.eq(PreSettlementChangeRecord::getPreSettlementId, id)
|
.eq(PreSettlementChangeRecord::getPreSettlementId, id)
|
||||||
.eq(PreSettlementChangeRecord::getIsDeleted, 0)
|
.eq(PreSettlementChangeRecord::getIsDeleted, 0)
|
||||||
@@ -346,6 +351,12 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
rebuildSummaryFees(settlement.getId(), true);
|
rebuildSummaryFees(settlement.getId(), true);
|
||||||
applySummaryRequest(settlement.getId(), contractChanged ? List.of() : request.getSummaryFees());
|
applySummaryRequest(settlement.getId(), contractChanged ? List.of() : request.getSummaryFees());
|
||||||
refreshSettlementAmount(settlement);
|
refreshSettlementAmount(settlement);
|
||||||
|
projectAdvanceWriteOffService.replaceWriteOffs(
|
||||||
|
TransportBusiConstant.SETTLEMENT_BILL_PRE,
|
||||||
|
settlement.getId(),
|
||||||
|
settlement.getContractId(),
|
||||||
|
settlement.getSettlementAmount(),
|
||||||
|
request.getProjectAdvanceWriteOffs());
|
||||||
if (created) {
|
if (created) {
|
||||||
saveChange(settlement.getId(), "结算单基本信息", null, "新增",
|
saveChange(settlement.getId(), "结算单基本信息", null, "新增",
|
||||||
"新增预结算单" + settlement.getPreSettlementNo(), "");
|
"新增预结算单" + settlement.getPreSettlementNo(), "");
|
||||||
@@ -362,6 +373,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
}
|
}
|
||||||
releaseSourceDetails(settlement, listDetails(id));
|
releaseSourceDetails(settlement, listDetails(id));
|
||||||
deleteChildren(id);
|
deleteChildren(id);
|
||||||
|
projectAdvanceWriteOffService.deleteBySettlement(TransportBusiConstant.SETTLEMENT_BILL_PRE, id);
|
||||||
deleteLogic(List.of(id));
|
deleteLogic(List.of(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-2
@@ -166,7 +166,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
|||||||
appendProjectChangeRecord(projectApply, CHANGE_TYPE_DRAFT, null, STATUS_DRAFT, "草稿",
|
appendProjectChangeRecord(projectApply, CHANGE_TYPE_DRAFT, null, STATUS_DRAFT, "草稿",
|
||||||
buildProjectChangeSnapshot(beforeRecord), buildProjectChangeSnapshot(projectApply));
|
buildProjectChangeSnapshot(beforeRecord), buildProjectChangeSnapshot(projectApply));
|
||||||
}
|
}
|
||||||
return saveOrUpdate(projectApply);
|
return persistProjectApply(projectApply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -185,7 +185,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
|||||||
projectApply.setApprovalStatus(STATUS_REVIEWING);
|
projectApply.setApprovalStatus(STATUS_REVIEWING);
|
||||||
projectApply.setCurrentNode(resolveApprovalNode(projectApply));
|
projectApply.setCurrentNode(resolveApprovalNode(projectApply));
|
||||||
projectApply.setCurrentProcessor("待处理");
|
projectApply.setCurrentProcessor("待处理");
|
||||||
return saveOrUpdate(projectApply);
|
return persistProjectApply(projectApply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -772,6 +772,8 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
|||||||
private void validateLength(ProjectApply projectApply) {
|
private void validateLength(ProjectApply projectApply) {
|
||||||
TransportBusinessSupport.validateLength(projectApply.getProjectName(), 50, "项目名称不能超过50个字");
|
TransportBusinessSupport.validateLength(projectApply.getProjectName(), 50, "项目名称不能超过50个字");
|
||||||
TransportBusinessSupport.validateLength(projectApply.getProjectShortName(), 20, "项目简称不能超过20个字");
|
TransportBusinessSupport.validateLength(projectApply.getProjectShortName(), 20, "项目简称不能超过20个字");
|
||||||
|
TransportBusinessSupport.validateLength(projectApply.getUndertakeDeptId(), 500, "平台公司选择过多,请减少后再提交");
|
||||||
|
TransportBusinessSupport.validateLength(projectApply.getUndertakeDeptName(), 500, "平台公司名称过长,请减少选择后再提交");
|
||||||
TransportBusinessSupport.validateLength(projectApply.getSourceRemark(), 300, "项目由来说明不能超过300个字");
|
TransportBusinessSupport.validateLength(projectApply.getSourceRemark(), 300, "项目由来说明不能超过300个字");
|
||||||
TransportBusinessSupport.validateLength(projectApply.getCargoQuantity(), 100, "预估货物数量不能超过100个字");
|
TransportBusinessSupport.validateLength(projectApply.getCargoQuantity(), 100, "预估货物数量不能超过100个字");
|
||||||
TransportBusinessSupport.validateLength(projectApply.getTransportRoute(), 100, "运输线路不能超过100个字");
|
TransportBusinessSupport.validateLength(projectApply.getTransportRoute(), 100, "运输线路不能超过100个字");
|
||||||
@@ -1023,6 +1025,37 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean persistProjectApply(ProjectApply projectApply) {
|
||||||
|
try {
|
||||||
|
return saveOrUpdate(projectApply);
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
throw translateUndertakeDeptPersistError(exception, projectApply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选平台公司写入失败时,提示执行字段扩容脚本(原列为 bigint / varchar(100))。
|
||||||
|
*/
|
||||||
|
private RuntimeException translateUndertakeDeptPersistError(RuntimeException exception, ProjectApply projectApply) {
|
||||||
|
String message = Func.toStr(exception.getMessage());
|
||||||
|
Throwable cause = exception.getCause();
|
||||||
|
while (cause != null) {
|
||||||
|
message = message + " " + Func.toStr(cause.getMessage());
|
||||||
|
cause = cause.getCause();
|
||||||
|
}
|
||||||
|
String lowerMessage = message.toLowerCase();
|
||||||
|
boolean multiUndertake = Func.isNotEmpty(projectApply.getUndertakeDeptId())
|
||||||
|
&& (projectApply.getUndertakeDeptId().contains(",") || projectApply.getUndertakeDeptId().contains(","));
|
||||||
|
boolean columnHint = lowerMessage.contains("undertake_dept")
|
||||||
|
|| lowerMessage.contains("incorrect integer")
|
||||||
|
|| lowerMessage.contains("data truncation")
|
||||||
|
|| lowerMessage.contains("data too long");
|
||||||
|
if (multiUndertake && columnHint) {
|
||||||
|
return new ServiceException("平台公司多选保存失败,请先执行脚本 blade_project_apply_undertake_dept_multi_20261014.sql 扩容字段后重试");
|
||||||
|
}
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
private void normalizeUndertakeDept(ProjectApply projectApply) {
|
private void normalizeUndertakeDept(ProjectApply projectApply) {
|
||||||
if (projectApply == null) {
|
if (projectApply == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+211
@@ -0,0 +1,211 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.system.cache.UserCache;
|
||||||
|
import org.springblade.transport.constant.TransportBusiConstant;
|
||||||
|
import org.springblade.transport.mapper.PaymentApplicationMapper;
|
||||||
|
import org.springblade.transport.mapper.SettlementProjectAdvanceWriteOffMapper;
|
||||||
|
import org.springblade.transport.pojo.dto.SettlementProjectAdvanceWriteOffRequest;
|
||||||
|
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||||
|
import org.springblade.transport.pojo.entity.SettlementProjectAdvanceWriteOff;
|
||||||
|
import org.springblade.transport.pojo.vo.ProjectAdvanceWriteOffOptionVO;
|
||||||
|
import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算单项目预付核销服务实现
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SettlementProjectAdvanceWriteOffServiceImpl implements ISettlementProjectAdvanceWriteOffService {
|
||||||
|
|
||||||
|
private final SettlementProjectAdvanceWriteOffMapper writeOffMapper;
|
||||||
|
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProjectAdvanceWriteOffOptionVO> listOptions(Long contractId, String excludeSettlementType,
|
||||||
|
Long excludeSettlementId) {
|
||||||
|
if (contractId == null) {
|
||||||
|
throw new ServiceException("请先选择合同");
|
||||||
|
}
|
||||||
|
List<PaymentApplication> applications = paymentApplicationMapper.selectList(
|
||||||
|
Wrappers.<PaymentApplication>lambdaQuery()
|
||||||
|
.eq(PaymentApplication::getContractId, contractId)
|
||||||
|
.eq(PaymentApplication::getPaymentType, TransportBusiConstant.PAYMENT_TYPE_PROJECT_ADVANCE)
|
||||||
|
.eq(PaymentApplication::getApprovalStatus, TransportBusiConstant.APPROVED)
|
||||||
|
.eq(PaymentApplication::getIsDeleted, 0)
|
||||||
|
.orderByDesc(PaymentApplication::getCreateTime));
|
||||||
|
List<ProjectAdvanceWriteOffOptionVO> options = new ArrayList<>();
|
||||||
|
for (PaymentApplication application : applications) {
|
||||||
|
BigDecimal projectAdvanceAmount = money(application.getAppliedAmount());
|
||||||
|
BigDecimal usedAmount = money(writeOffMapper.sumWriteOffByPaymentApplication(
|
||||||
|
application.getId(), excludeSettlementType, excludeSettlementId));
|
||||||
|
BigDecimal availableAmount = projectAdvanceAmount.subtract(usedAmount).max(BigDecimal.ZERO);
|
||||||
|
if (availableAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ProjectAdvanceWriteOffOptionVO option = new ProjectAdvanceWriteOffOptionVO();
|
||||||
|
option.setPaymentApplicationId(application.getId());
|
||||||
|
option.setPaymentNo(application.getPaymentNo());
|
||||||
|
option.setProjectAdvanceAmount(projectAdvanceAmount);
|
||||||
|
option.setAvailableWriteOffAmount(availableAmount);
|
||||||
|
option.setKingdeeBillNo(application.getKingdeeBillNo());
|
||||||
|
option.setCreateUserName(UserCache.getUserRealName(application.getCreateUser()));
|
||||||
|
option.setCreateTime(application.getCreateTime());
|
||||||
|
options.add(option);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SettlementProjectAdvanceWriteOff> listBySettlement(String settlementType, Long settlementId) {
|
||||||
|
if (Func.isEmpty(settlementType) || settlementId == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<SettlementProjectAdvanceWriteOff> rows = writeOffMapper.selectList(
|
||||||
|
Wrappers.<SettlementProjectAdvanceWriteOff>lambdaQuery()
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementType, settlementType)
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementId, settlementId)
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getIsDeleted, 0)
|
||||||
|
.orderByAsc(SettlementProjectAdvanceWriteOff::getCreateTime)
|
||||||
|
.orderByAsc(SettlementProjectAdvanceWriteOff::getId));
|
||||||
|
for (SettlementProjectAdvanceWriteOff row : rows) {
|
||||||
|
PaymentApplication application = paymentApplicationMapper.selectById(row.getPaymentApplicationId());
|
||||||
|
BigDecimal projectAdvanceAmount = application == null
|
||||||
|
? money(row.getProjectAdvanceAmount()) : money(application.getAppliedAmount());
|
||||||
|
BigDecimal usedAmount = money(writeOffMapper.sumWriteOffByPaymentApplication(
|
||||||
|
row.getPaymentApplicationId(), settlementType, settlementId));
|
||||||
|
row.setProjectAdvanceAmount(projectAdvanceAmount);
|
||||||
|
row.setAvailableWriteOffAmount(projectAdvanceAmount.subtract(usedAmount).max(BigDecimal.ZERO));
|
||||||
|
if (application != null) {
|
||||||
|
row.setPaymentNo(application.getPaymentNo());
|
||||||
|
row.setKingdeeBillNo(application.getKingdeeBillNo());
|
||||||
|
row.setCreateUserName(UserCache.getUserRealName(application.getCreateUser()));
|
||||||
|
row.setPaymentCreateTime(application.getCreateTime());
|
||||||
|
} else {
|
||||||
|
row.setCreateUserName(UserCache.getUserRealName(row.getCreateUser()));
|
||||||
|
row.setPaymentCreateTime(row.getCreateTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal sumBySettlement(String settlementType, Long settlementId) {
|
||||||
|
if (Func.isEmpty(settlementType) || settlementId == null) {
|
||||||
|
return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
return money(writeOffMapper.sumWriteOffBySettlement(settlementType, settlementId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void replaceWriteOffs(String settlementType, Long settlementId, Long contractId,
|
||||||
|
BigDecimal settlementAmount, List<SettlementProjectAdvanceWriteOffRequest> writeOffs) {
|
||||||
|
if (Func.isEmpty(settlementType) || settlementId == null) {
|
||||||
|
throw new ServiceException("结算单不能为空");
|
||||||
|
}
|
||||||
|
if (contractId == null) {
|
||||||
|
throw new ServiceException("合同不能为空");
|
||||||
|
}
|
||||||
|
List<SettlementProjectAdvanceWriteOffRequest> requestRows = writeOffs == null ? List.of() : writeOffs;
|
||||||
|
BigDecimal settlementLimit = money(settlementAmount);
|
||||||
|
BigDecimal totalWriteOff = BigDecimal.ZERO;
|
||||||
|
Set<Long> paymentIds = new HashSet<>();
|
||||||
|
List<SettlementProjectAdvanceWriteOff> entities = new ArrayList<>();
|
||||||
|
for (SettlementProjectAdvanceWriteOffRequest request : requestRows) {
|
||||||
|
if (request == null || request.getPaymentApplicationId() == null) {
|
||||||
|
throw new ServiceException("请选择项目预付单");
|
||||||
|
}
|
||||||
|
if (!paymentIds.add(request.getPaymentApplicationId())) {
|
||||||
|
throw new ServiceException("同一项目预付单不可重复核销");
|
||||||
|
}
|
||||||
|
BigDecimal writeOffAmount = money(request.getWriteOffAmount());
|
||||||
|
if (writeOffAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("本次核销金额必须大于0");
|
||||||
|
}
|
||||||
|
PaymentApplication application = paymentApplicationMapper.selectById(request.getPaymentApplicationId());
|
||||||
|
if (application == null || Objects.equals(application.getIsDeleted(), 1)) {
|
||||||
|
throw new ServiceException("项目预付单不存在");
|
||||||
|
}
|
||||||
|
if (!TransportBusiConstant.PAYMENT_TYPE_PROJECT_ADVANCE.equals(application.getPaymentType())) {
|
||||||
|
throw new ServiceException("仅可核销项目预付单");
|
||||||
|
}
|
||||||
|
if (!TransportBusiConstant.APPROVED.equals(application.getApprovalStatus())) {
|
||||||
|
throw new ServiceException("仅可选择审核通过的项目预付单");
|
||||||
|
}
|
||||||
|
if (!Objects.equals(contractId, application.getContractId())) {
|
||||||
|
throw new ServiceException("项目预付单与结算单合同不一致");
|
||||||
|
}
|
||||||
|
BigDecimal projectAdvanceAmount = money(application.getAppliedAmount());
|
||||||
|
BigDecimal usedAmount = money(writeOffMapper.sumWriteOffByPaymentApplication(
|
||||||
|
application.getId(), settlementType, settlementId));
|
||||||
|
BigDecimal availableAmount = projectAdvanceAmount.subtract(usedAmount).max(BigDecimal.ZERO);
|
||||||
|
if (writeOffAmount.compareTo(availableAmount) > 0) {
|
||||||
|
throw new ServiceException("预付单" + Func.toStr(application.getPaymentNo(), "")
|
||||||
|
+ "本次核销金额不能超过可核销金额");
|
||||||
|
}
|
||||||
|
totalWriteOff = totalWriteOff.add(writeOffAmount);
|
||||||
|
SettlementProjectAdvanceWriteOff entity = new SettlementProjectAdvanceWriteOff();
|
||||||
|
entity.setSettlementType(settlementType);
|
||||||
|
entity.setSettlementId(settlementId);
|
||||||
|
entity.setPaymentApplicationId(application.getId());
|
||||||
|
entity.setPaymentNo(application.getPaymentNo());
|
||||||
|
entity.setProjectAdvanceAmount(projectAdvanceAmount);
|
||||||
|
entity.setWriteOffAmount(writeOffAmount);
|
||||||
|
entity.setKingdeeBillNo(application.getKingdeeBillNo());
|
||||||
|
entity.setStatus(1);
|
||||||
|
entity.setIsDeleted(0);
|
||||||
|
entities.add(entity);
|
||||||
|
}
|
||||||
|
if (totalWriteOff.compareTo(settlementLimit) > 0) {
|
||||||
|
throw new ServiceException("项目预付核销合计不能超过结算单金额");
|
||||||
|
}
|
||||||
|
writeOffMapper.delete(Wrappers.<SettlementProjectAdvanceWriteOff>lambdaQuery()
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementType, settlementType)
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementId, settlementId));
|
||||||
|
for (SettlementProjectAdvanceWriteOff entity : entities) {
|
||||||
|
writeOffMapper.insert(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteBySettlement(String settlementType, Long settlementId) {
|
||||||
|
if (Func.isEmpty(settlementType) || settlementId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeOffMapper.delete(Wrappers.<SettlementProjectAdvanceWriteOff>lambdaQuery()
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementType, settlementType)
|
||||||
|
.eq(SettlementProjectAdvanceWriteOff::getSettlementId, settlementId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal money(BigDecimal value) {
|
||||||
|
return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
|
||||||
|
: value.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,64 @@
|
|||||||
-- 平台公司支持多选:ID 逗号分隔,名称顿号分隔
|
-- 项目立项平台公司多选:ID 逗号分隔,名称顿号分隔
|
||||||
ALTER TABLE `blade_project_apply`
|
-- 原字段:undertake_dept_id bigint、undertake_dept_name varchar(100)
|
||||||
MODIFY COLUMN `undertake_dept_id` varchar(500) DEFAULT NULL COMMENT '平台公司ID,多个逗号分隔',
|
-- 选择 2 个及以上平台公司时,若未扩容会报 Incorrect integer value / Data truncation
|
||||||
MODIFY COLUMN `undertake_dept_name` varchar(500) DEFAULT NULL COMMENT '平台公司,多个顿号分隔';
|
-- 整列索引挂在 varchar(500) 上可能因键长超限导致 ALTER 失败,故先删索引再改前缀索引
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS `upgrade_project_apply_undertake_dept_multi_20261014`$$
|
||||||
|
CREATE PROCEDURE `upgrade_project_apply_undertake_dept_multi_20261014`()
|
||||||
|
BEGIN
|
||||||
|
DECLARE db_name varchar(128);
|
||||||
|
DECLARE need_modify tinyint DEFAULT 0;
|
||||||
|
SET db_name = DATABASE();
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_schema = db_name
|
||||||
|
AND table_name = 'blade_project_apply'
|
||||||
|
AND column_name = 'undertake_dept_id'
|
||||||
|
AND (
|
||||||
|
data_type IN ('bigint', 'int', 'integer')
|
||||||
|
OR (data_type IN ('varchar', 'char') AND IFNULL(character_maximum_length, 0) < 500)
|
||||||
|
)
|
||||||
|
) OR EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_schema = db_name
|
||||||
|
AND table_name = 'blade_project_apply'
|
||||||
|
AND column_name = 'undertake_dept_name'
|
||||||
|
AND data_type IN ('varchar', 'char')
|
||||||
|
AND IFNULL(character_maximum_length, 0) < 500
|
||||||
|
) THEN
|
||||||
|
SET need_modify = 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF need_modify = 1 THEN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.statistics
|
||||||
|
WHERE table_schema = db_name
|
||||||
|
AND table_name = 'blade_project_apply'
|
||||||
|
AND index_name = 'idx_project_apply_dept'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE `blade_project_apply` DROP INDEX `idx_project_apply_dept`;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ALTER TABLE `blade_project_apply`
|
||||||
|
MODIFY COLUMN `undertake_dept_id` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '平台公司ID,多个逗号分隔',
|
||||||
|
MODIFY COLUMN `undertake_dept_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '平台公司,多个顿号分隔';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.statistics
|
||||||
|
WHERE table_schema = db_name
|
||||||
|
AND table_name = 'blade_project_apply'
|
||||||
|
AND index_name = 'idx_project_apply_dept'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE `blade_project_apply`
|
||||||
|
ADD INDEX `idx_project_apply_dept` (`undertake_dept_id`(191)) USING BTREE;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
CALL `upgrade_project_apply_undertake_dept_multi_20261014`()$$
|
||||||
|
DROP PROCEDURE `upgrade_project_apply_undertake_dept_multi_20261014`$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ CREATE TABLE `blade_project_apply` (
|
|||||||
`void_reason` varchar(500) DEFAULT NULL COMMENT '作废原因',
|
`void_reason` varchar(500) DEFAULT NULL COMMENT '作废原因',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
UNIQUE KEY `uk_project_apply_name` (`project_name`, `is_deleted`) USING BTREE,
|
UNIQUE KEY `uk_project_apply_name` (`project_name`, `is_deleted`) USING BTREE,
|
||||||
KEY `idx_project_apply_dept` (`undertake_dept_id`) USING BTREE,
|
KEY `idx_project_apply_dept` (`undertake_dept_id`(191)) USING BTREE,
|
||||||
KEY `idx_project_apply_status` (`approval_status`) USING BTREE,
|
KEY `idx_project_apply_status` (`approval_status`) USING BTREE,
|
||||||
KEY `idx_project_apply_create_time` (`create_time`) USING BTREE
|
KEY `idx_project_apply_create_time` (`create_time`) USING BTREE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目立项';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目立项';
|
||||||
@@ -80,8 +80,8 @@ CREATE TABLE `blade_temporary_credit_limit` (
|
|||||||
`project_id` bigint(20) DEFAULT NULL COMMENT '项目ID',
|
`project_id` bigint(20) DEFAULT NULL COMMENT '项目ID',
|
||||||
`project_code` varchar(100) DEFAULT NULL COMMENT '项目编号',
|
`project_code` varchar(100) DEFAULT NULL COMMENT '项目编号',
|
||||||
`project_name` varchar(100) DEFAULT NULL COMMENT '项目名称',
|
`project_name` varchar(100) DEFAULT NULL COMMENT '项目名称',
|
||||||
`undertake_dept_id` varchar(500) DEFAULT NULL COMMENT '平台公司ID,多个逗号分隔',
|
`undertake_dept_id` bigint(20) DEFAULT NULL COMMENT '承办部门ID',
|
||||||
`undertake_dept_name` varchar(500) DEFAULT NULL COMMENT '平台公司,多个顿号分隔',
|
`undertake_dept_name` varchar(100) DEFAULT NULL COMMENT '承办部门',
|
||||||
`project_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '项目资金使用额度(万元)',
|
`project_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '项目资金使用额度(万元)',
|
||||||
`used_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '已使用项目资金额度(万元)',
|
`used_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '已使用项目资金额度(万元)',
|
||||||
`remaining_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '剩余项目资金使用额度(万元)',
|
`remaining_fund_limit` decimal(18,2) DEFAULT NULL COMMENT '剩余项目资金使用额度(万元)',
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- 结算单项目预付核销明细
|
||||||
|
CREATE TABLE IF NOT EXISTS `blade_settlement_project_advance_write_off` (
|
||||||
|
`id` bigint NOT NULL COMMENT '主键',
|
||||||
|
`tenant_id` varchar(12) NOT NULL DEFAULT '000000' COMMENT '租户ID',
|
||||||
|
`settlement_type` varchar(20) NOT NULL COMMENT '结算单类型 pre/formal',
|
||||||
|
`settlement_id` bigint NOT NULL COMMENT '预结算/正式结算单ID',
|
||||||
|
`payment_application_id` bigint NOT NULL COMMENT '项目预付付款申请ID',
|
||||||
|
`payment_no` varchar(64) DEFAULT NULL COMMENT '预付单号',
|
||||||
|
`project_advance_amount` decimal(18,2) DEFAULT 0.00 COMMENT '项目预付金额',
|
||||||
|
`write_off_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '本次核销金额',
|
||||||
|
`kingdee_bill_no` varchar(100) DEFAULT NULL COMMENT '金蝶预付单号',
|
||||||
|
`create_user` bigint DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_user` bigint DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`status` int DEFAULT 1 COMMENT '业务状态',
|
||||||
|
`is_deleted` int DEFAULT 0 COMMENT '是否已删除',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_spa_write_off_settlement` (`settlement_type`,`settlement_id`),
|
||||||
|
KEY `idx_spa_write_off_payment` (`payment_application_id`),
|
||||||
|
KEY `idx_spa_write_off_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='结算单项目预付核销';
|
||||||
Reference in New Issue
Block a user