应付配置增加附加费配置,应付账单新增核销、对账、凭证、审核功能
This commit is contained in:
@@ -33,9 +33,23 @@ public class PayBill
|
||||
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
/** DRAFT CONFIRMED PAID VOID */
|
||||
private BigDecimal supplierAmount;
|
||||
|
||||
private BigDecimal diffAmount;
|
||||
|
||||
private BigDecimal paidAmount;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date dueDate;
|
||||
|
||||
/** DRAFT PENDING CONFIRMED PARTIAL PAID RED VOID */
|
||||
private String status;
|
||||
|
||||
/** BLUE 蓝字,RED 红字 */
|
||||
private String billKind;
|
||||
|
||||
private Long originalBillId;
|
||||
|
||||
private String voidReason;
|
||||
|
||||
private String voidBy;
|
||||
@@ -43,6 +57,29 @@ public class PayBill
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date voidTime;
|
||||
|
||||
private String confirmBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date confirmTime;
|
||||
|
||||
/** 0.06 表示 6% */
|
||||
private BigDecimal taxRate;
|
||||
|
||||
private BigDecimal amountNet;
|
||||
|
||||
private BigDecimal taxAmount;
|
||||
|
||||
private BigDecimal exchangeRate;
|
||||
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date rateDate;
|
||||
|
||||
private Integer termDays;
|
||||
|
||||
private String submitBy;
|
||||
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@@ -58,4 +95,10 @@ public class PayBill
|
||||
private List<PayBillLine> lines;
|
||||
|
||||
private List<PayBillLog> logs;
|
||||
|
||||
private List<PayBillDiff> diffs;
|
||||
|
||||
private List<PayBillFile> files;
|
||||
|
||||
private List<PayBillApproval> approvals;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应付账单审核记录。 */
|
||||
@Data
|
||||
public class PayBillApproval
|
||||
{
|
||||
private Long approvalId;
|
||||
|
||||
private Long billId;
|
||||
|
||||
/** SUBMIT APPROVE REJECT */
|
||||
private String action;
|
||||
|
||||
private String opinion;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -32,4 +32,34 @@ public class PayBillCommand
|
||||
|
||||
/** 要作废的应付账单ID */
|
||||
private Long billId;
|
||||
|
||||
/** 到期日,yyyy-MM-dd */
|
||||
private String dueDate;
|
||||
|
||||
/** 账单费用行。登记对方金额时使用 */
|
||||
private java.util.List<PayBillLine> lines;
|
||||
|
||||
/** 要处理的账单行 */
|
||||
private Long lineId;
|
||||
|
||||
/** 对方金额 */
|
||||
private java.math.BigDecimal supplierAmount;
|
||||
|
||||
/** OURS 接受我方,THEIRS 接受对方 */
|
||||
private String handleType;
|
||||
|
||||
/** 差异原因或红字原因 */
|
||||
private String reason;
|
||||
|
||||
/** 驳回意见 */
|
||||
private String opinion;
|
||||
|
||||
/** 税率,0.06 表示 6% */
|
||||
private java.math.BigDecimal taxRate;
|
||||
|
||||
/** 折人民币汇率 */
|
||||
private java.math.BigDecimal exchangeRate;
|
||||
|
||||
/** 汇率日期,yyyy-MM-dd */
|
||||
private String rateDate;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应付账单上的一行对账差异。 */
|
||||
@Data
|
||||
public class PayBillDiff
|
||||
{
|
||||
private Long diffId;
|
||||
|
||||
private Long billId;
|
||||
|
||||
private Long lineId;
|
||||
|
||||
private String feeId;
|
||||
|
||||
private BigDecimal ourAmount;
|
||||
|
||||
private BigDecimal supplierAmount;
|
||||
|
||||
private BigDecimal diffAmount;
|
||||
|
||||
private String reason;
|
||||
|
||||
/** OPEN ACCEPT_OURS ACCEPT_THEIRS */
|
||||
private String handleType;
|
||||
|
||||
private String adjustFeeId;
|
||||
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应付凭证。发票、对账单或费用通知挂在应付账单上。 */
|
||||
@Data
|
||||
public class PayBillFile
|
||||
{
|
||||
private Long fileId;
|
||||
|
||||
private Long billId;
|
||||
|
||||
private String billNo;
|
||||
|
||||
private String supplierName;
|
||||
|
||||
/** INVOICE STATEMENT DEBIT OTHER */
|
||||
private String fileType;
|
||||
|
||||
private String invoiceNo;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date invoiceDate;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private BigDecimal taxAmount;
|
||||
|
||||
private BigDecimal amountNet;
|
||||
|
||||
private String sellerName;
|
||||
|
||||
private BigDecimal matchedAmount;
|
||||
|
||||
private String currency;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private String fileUrl;
|
||||
|
||||
private String deleted;
|
||||
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -13,6 +13,8 @@ public class PayBillLine
|
||||
|
||||
private Long billId;
|
||||
|
||||
private String feeId;
|
||||
|
||||
private String orderBillId;
|
||||
|
||||
private String orderNo;
|
||||
@@ -23,8 +25,27 @@ public class PayBillLine
|
||||
|
||||
private String feeName;
|
||||
|
||||
private String feeSource;
|
||||
|
||||
private String feeLevel;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private BigDecimal supplierAmount;
|
||||
|
||||
private BigDecimal diffAmount;
|
||||
|
||||
/** NONE OPEN ACCEPT_OURS ACCEPT_THEIRS */
|
||||
private String diffStatus;
|
||||
|
||||
private BigDecimal taxRate;
|
||||
|
||||
private BigDecimal amountNet;
|
||||
|
||||
private BigDecimal taxAmount;
|
||||
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
private String currency;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
@@ -12,6 +13,14 @@ public class PayBillLog
|
||||
|
||||
private Long billId;
|
||||
|
||||
private String bizType;
|
||||
|
||||
private Long bizId;
|
||||
|
||||
private Long lineId;
|
||||
|
||||
private String feeId;
|
||||
|
||||
private String operationType;
|
||||
|
||||
private String fromStatus;
|
||||
@@ -20,6 +29,10 @@ public class PayBillLog
|
||||
|
||||
private String operationDesc;
|
||||
|
||||
private BigDecimal amountBefore;
|
||||
|
||||
private BigDecimal amountAfter;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
|
||||
@@ -45,4 +45,10 @@ public class PayBillOrder
|
||||
|
||||
/** 已挂在未作废账单上时的账单号 */
|
||||
private String occupiedBillNo;
|
||||
|
||||
/** 未删除费用行数 */
|
||||
private Integer feeCount;
|
||||
|
||||
/** 还没进入未作废账单的费用行数 */
|
||||
private Integer unbilledCount;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应付付款单。一张付款可以核销多张已确认账单。 */
|
||||
@Data
|
||||
public class PayPayment
|
||||
{
|
||||
private Long paymentId;
|
||||
|
||||
private String paymentNo;
|
||||
|
||||
private Long supplierId;
|
||||
|
||||
private String supplierName;
|
||||
|
||||
private String currency;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date payDate;
|
||||
|
||||
/** BANK CASH OTHER */
|
||||
private String payMethod;
|
||||
|
||||
private String bankAccount;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private BigDecimal bankFee;
|
||||
|
||||
private BigDecimal exchangeRate;
|
||||
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
private BigDecimal allocAmount;
|
||||
|
||||
/** DRAFT PAID VOID REFUNDED */
|
||||
private String status;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
private String confirmBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date confirmTime;
|
||||
|
||||
private List<PayPaymentAlloc> allocs;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应付付款核销到一张账单的金额。 */
|
||||
@Data
|
||||
public class PayPaymentAlloc
|
||||
{
|
||||
private Long allocId;
|
||||
|
||||
private Long paymentId;
|
||||
|
||||
private Long billId;
|
||||
|
||||
private String billNo;
|
||||
|
||||
private BigDecimal allocAmount;
|
||||
|
||||
/** ACTIVE REVERSED */
|
||||
private String allocStatus;
|
||||
|
||||
/** 账单未核销金额,只在查询未付账单时带出 */
|
||||
private BigDecimal openAmount;
|
||||
|
||||
private String currency;
|
||||
|
||||
private Long supplierId;
|
||||
|
||||
private String supplierName;
|
||||
}
|
||||
@@ -151,5 +151,8 @@ public class PayProduct extends BaseEntity
|
||||
|
||||
private List<PaySurcharge> surcharges;
|
||||
|
||||
/** 这一版附加费配置。只在计费时带上,不入库 */
|
||||
private List<PaySurchargeRule> billingRules;
|
||||
|
||||
private List<PayExtraLine> extraLines;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 应付附加费计费条件。同一产品版本的全部条件都在这张表,用 calcRule 区分列表。
|
||||
*/
|
||||
@Data
|
||||
public class PaySurchargeRule
|
||||
{
|
||||
private Long ruleId;
|
||||
|
||||
/** 这一版应付产品 */
|
||||
private Long productId;
|
||||
|
||||
/** 价卡版本周期 */
|
||||
private Long termId;
|
||||
|
||||
private Integer versionNo;
|
||||
|
||||
/** ALWAYS、POSTCODE、ZONE、EXTRA_KG、WEIGHT、SIZE、RETURN */
|
||||
private String calcRule;
|
||||
|
||||
/** 1开 0关 */
|
||||
private String enabled;
|
||||
|
||||
/** 条件摘要,由明细生成 */
|
||||
private String calcParam;
|
||||
|
||||
/** 条件明细 JSON。接口不直接收发这一列,用 detail */
|
||||
@JsonIgnore
|
||||
private String ruleDetail;
|
||||
|
||||
/** 这一条规则的结构化参数。超尺寸含长宽高、围长、体积、加收比例和命中方式 */
|
||||
private Map<String, Object> detail;
|
||||
|
||||
/** 续重行的邮编 */
|
||||
private String postcodeParam;
|
||||
|
||||
private String zoneName;
|
||||
|
||||
private BigDecimal weightMin;
|
||||
|
||||
private BigDecimal weightMax;
|
||||
|
||||
private Integer sortNo;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 把同一套计费条件写到若干产品的当前版本。每个版本只保留这一份。
|
||||
*/
|
||||
@Data
|
||||
public class PaySurchargeRuleSave
|
||||
{
|
||||
private List<PaySurchargeRuleBind> products = new ArrayList<>();
|
||||
|
||||
private List<PaySurchargeRule> rules = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
public static class PaySurchargeRuleBind
|
||||
{
|
||||
private Long productId;
|
||||
|
||||
private Long termId;
|
||||
|
||||
private Integer versionNo;
|
||||
|
||||
private String productName;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,14 @@ public class PrcPayBillFee implements Serializable
|
||||
|
||||
/** 是否删除:0未删除 1已删除 */
|
||||
private String isDeleted;
|
||||
|
||||
/** 已进入的应付账单行。空表示还没出账 */
|
||||
private Long statementLineId;
|
||||
|
||||
/** 结算供应商。空表示还没标明付款对象 */
|
||||
private Long settleSupplierId;
|
||||
|
||||
private String settleSupplierName;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ package com.bms.price.mapper;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PayBill;
|
||||
import com.bms.price.domain.PayBillApproval;
|
||||
import com.bms.price.domain.PayBillDiff;
|
||||
import com.bms.price.domain.PayBillFile;
|
||||
import com.bms.price.domain.PayBillLine;
|
||||
import com.bms.price.domain.PayBillLog;
|
||||
import com.bms.price.domain.PayBillOrder;
|
||||
import com.bms.price.domain.PayPaymentAlloc;
|
||||
import com.bms.price.domain.PrcPayBillFee;
|
||||
|
||||
/** 供应商应付账单 pay_bill 及其明细、日志。 */
|
||||
@@ -63,6 +67,22 @@ public interface PayBillMapper
|
||||
*/
|
||||
List<PrcPayBillFee> selectOrderFees(@Param("billIds") List<String> billIds);
|
||||
|
||||
/**
|
||||
* 读取还没被未作废账单占用的费用。旧账单没有费用行号时,整票视为已占用。
|
||||
*
|
||||
* @param billIds 计费订单ID
|
||||
* @return 可抄进本次账单的费用
|
||||
*/
|
||||
List<PrcPayBillFee> selectFreeFees(@Param("billIds") List<String> billIds);
|
||||
|
||||
/**
|
||||
* 这票订单还有没有未删除的计算费用。
|
||||
*
|
||||
* @param billId 计费订单ID
|
||||
* @return 计算费用条数
|
||||
*/
|
||||
int countCalcFees(@Param("billId") String billId);
|
||||
|
||||
/**
|
||||
* 插入一张草稿应付账单。
|
||||
*
|
||||
@@ -87,6 +107,103 @@ public interface PayBillMapper
|
||||
*/
|
||||
int insertLines(@Param("list") List<PayBillLine> list);
|
||||
|
||||
/**
|
||||
* 写入一条账单费用快照,并回填行号。
|
||||
*
|
||||
* @param line 费用行
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertLine(PayBillLine line);
|
||||
|
||||
/**
|
||||
* 把费用明细占用到刚写入的账单行。已被占用时影响行数为 0。
|
||||
*
|
||||
* @param feeId 费用明细ID
|
||||
* @param lineId 账单行ID
|
||||
* @return 影响行数
|
||||
*/
|
||||
int bindFeeLine(@Param("feeId") String feeId, @Param("lineId") Long lineId);
|
||||
|
||||
/**
|
||||
* 释放一张账单占用的费用明细。
|
||||
*
|
||||
* @param billId 应付账单ID
|
||||
* @return 影响行数
|
||||
*/
|
||||
int clearFeeLines(@Param("billId") Long billId);
|
||||
|
||||
/**
|
||||
* 释放某一条账单行占用的费用明细。
|
||||
*
|
||||
* @param lineId 账单行ID
|
||||
* @return 影响行数
|
||||
*/
|
||||
int clearFeeLine(@Param("lineId") Long lineId);
|
||||
|
||||
/**
|
||||
* 按费用行重算账单金额、订单数,并写回状态。
|
||||
*
|
||||
* @param bill 账单ID、金额、状态和更新人
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updatePayBillMoney(PayBill bill);
|
||||
|
||||
/**
|
||||
* 删除草稿中的一条费用快照。
|
||||
*
|
||||
* @param billId 应付账单ID
|
||||
* @param lineId 账单行ID
|
||||
* @return 影响行数
|
||||
*/
|
||||
int deleteLine(@Param("billId") Long billId, @Param("lineId") Long lineId);
|
||||
|
||||
int updateLineAmount(PayBillLine line);
|
||||
|
||||
int deleteOpenDiff(@Param("lineId") Long lineId);
|
||||
|
||||
/**
|
||||
* 读取一张账单的差异、凭证和审核。
|
||||
*
|
||||
* @param billId 应付账单ID
|
||||
* @return 对应记录
|
||||
*/
|
||||
List<PayBillDiff> selectDiffs(@Param("billId") Long billId);
|
||||
|
||||
List<PayBillFile> selectFiles(@Param("billId") Long billId);
|
||||
|
||||
List<PayBillApproval> selectApprovals(@Param("billId") Long billId);
|
||||
|
||||
PayBillLine selectLine(@Param("lineId") Long lineId);
|
||||
|
||||
PayBillDiff selectOpenDiff(@Param("lineId") Long lineId);
|
||||
|
||||
int insertDiff(PayBillDiff diff);
|
||||
|
||||
int updateDiff(PayBillDiff diff);
|
||||
|
||||
int deleteDiffsByLine(@Param("lineId") Long lineId);
|
||||
|
||||
int insertFile(PayBillFile file);
|
||||
|
||||
PayBillFile selectFile(@Param("fileId") Long fileId);
|
||||
|
||||
int markFileDeleted(@Param("fileId") Long fileId);
|
||||
|
||||
int insertApproval(PayBillApproval approval);
|
||||
|
||||
List<PayBillFile> selectFileList(PayBillFile query);
|
||||
|
||||
int countActiveFiles(@Param("billId") Long billId);
|
||||
|
||||
/**
|
||||
* 查询可以核销的账单。已确认或部分付款,且还有未付金额。
|
||||
*
|
||||
* @param supplierId 供应商
|
||||
* @param currency 币种
|
||||
* @return 未付账单
|
||||
*/
|
||||
List<PayPaymentAlloc> selectOpenBills(@Param("supplierId") Long supplierId, @Param("currency") String currency);
|
||||
|
||||
/**
|
||||
* 写入一条账单操作日志。
|
||||
*
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/** 应付价税、发票勾稽和费用付款对象。 */
|
||||
public interface PayFinanceMapper
|
||||
{
|
||||
int updateFinanceHead(@Param("billId") Long billId, @Param("taxRate") BigDecimal taxRate,
|
||||
@Param("exchangeRate") BigDecimal exchangeRate, @Param("rateDate") Date rateDate);
|
||||
|
||||
int updateSubmitBy(@Param("billId") Long billId, @Param("submitBy") String submitBy);
|
||||
|
||||
int updateLineTax(@Param("billId") Long billId, @Param("taxRate") BigDecimal taxRate,
|
||||
@Param("exchangeRate") BigDecimal exchangeRate);
|
||||
|
||||
int countDuplicateInvoice(@Param("supplierId") Long supplierId, @Param("invoiceNo") String invoiceNo,
|
||||
@Param("fileId") Long fileId);
|
||||
|
||||
int markInvoiceMatched(@Param("billId") Long billId);
|
||||
|
||||
Long selectLadingSupplier(@Param("ladingNo") String ladingNo);
|
||||
|
||||
int stampFee(@Param("feeId") String feeId, @Param("supplierId") Long supplierId, @Param("supplierName") String supplierName);
|
||||
|
||||
int stampCalc(@Param("billId") String billId, @Param("supplierId") Long supplierId, @Param("supplierName") String supplierName);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PayPayment;
|
||||
import com.bms.price.domain.PayPaymentAlloc;
|
||||
|
||||
/** 应付付款单和核销。 */
|
||||
public interface PayPaymentMapper
|
||||
{
|
||||
List<PayPayment> selectPayPaymentList(PayPayment query);
|
||||
|
||||
PayPayment selectPayPaymentById(@Param("paymentId") Long paymentId);
|
||||
|
||||
List<PayPaymentAlloc> selectAllocs(@Param("paymentId") Long paymentId);
|
||||
|
||||
int insertPayPayment(PayPayment payment);
|
||||
|
||||
int insertAlloc(PayPaymentAlloc alloc);
|
||||
|
||||
int updatePayPaymentStatus(PayPayment payment);
|
||||
|
||||
int reverseAlloc(@Param("allocId") Long allocId, @Param("reverseBy") String reverseBy);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PaySurchargeRule;
|
||||
|
||||
/** 应付附加费计费条件。一个产品版本一份。 */
|
||||
public interface PaySurchargeRuleMapper
|
||||
{
|
||||
List<PaySurchargeRule> selectByProduct(@Param("productId") Long productId, @Param("termId") Long termId);
|
||||
|
||||
int deleteByTerm(@Param("productId") Long productId, @Param("termId") Long termId);
|
||||
|
||||
int insertBatch(@Param("list") List<PaySurchargeRule> rules);
|
||||
|
||||
/** 同一产品字典下,比当前版本号小的最近一版 */
|
||||
PaySurchargeRule selectPreviousVersion(@Param("productId") Long productId);
|
||||
}
|
||||
@@ -9,12 +9,14 @@ import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.price.domain.PayCostLine;
|
||||
import com.bms.price.domain.PayOrderCostResult;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
import com.bms.price.domain.PaySurcharge;
|
||||
import com.bms.price.domain.PaySurchargeRule;
|
||||
import com.bms.price.domain.PayWeightBand;
|
||||
|
||||
/**
|
||||
@@ -69,7 +71,7 @@ public final class PayCostCalculator
|
||||
public static PayOrderCostResult calculate(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus)
|
||||
{
|
||||
return calculate(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, null);
|
||||
return calculate(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, null, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +91,16 @@ public final class PayCostCalculator
|
||||
*/
|
||||
public static PayOrderCostResult calculate(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus, List<PayRemoteBook.Hit> remoteHits)
|
||||
{
|
||||
return calculate(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, remoteHits, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* postcodeOn、zoneOn 来自附加费配置的两个开关。关掉时不再按价卡上的邮编或分区条件加附加费。
|
||||
*/
|
||||
public static PayOrderCostResult calculate(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus, List<PayRemoteBook.Hit> remoteHits,
|
||||
boolean postcodeOn, boolean zoneOn)
|
||||
{
|
||||
PayOrderCostResult result = new PayOrderCostResult();
|
||||
result.setFees(new ArrayList<>());
|
||||
@@ -146,7 +158,7 @@ public final class PayCostCalculator
|
||||
result.getFees().add(freightLine);
|
||||
result.setCurrency(currency);
|
||||
addSurcharges(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, freight, currency,
|
||||
band.getSectionName(), remoteHits, result);
|
||||
band.getSectionName(), remoteHits, postcodeOn, zoneOn, result);
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (PayCostLine fee : result.getFees())
|
||||
{
|
||||
@@ -266,7 +278,7 @@ public final class PayCostCalculator
|
||||
*/
|
||||
private static void addSurcharges(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus, BigDecimal freight, String currency,
|
||||
String freightSection, List<PayRemoteBook.Hit> remoteHits, PayOrderCostResult result)
|
||||
String freightSection, List<PayRemoteBook.Hit> remoteHits, boolean postcodeOn, boolean zoneOn, PayOrderCostResult result)
|
||||
{
|
||||
if (product.getSurcharges() == null)
|
||||
{
|
||||
@@ -283,6 +295,7 @@ public final class PayCostCalculator
|
||||
if (remoteHits != null && remoteFee(fee))
|
||||
{
|
||||
String name = blank(fee.getFeeName());
|
||||
boolean added = false;
|
||||
if (postcode == null || postcode.isBlank())
|
||||
{
|
||||
result.getSkipped().add(name + ":订单没有邮编");
|
||||
@@ -302,7 +315,21 @@ public final class PayCostCalculator
|
||||
{
|
||||
addFlat(result.getFees(), fee, currency, remark);
|
||||
}
|
||||
added = true;
|
||||
}
|
||||
if (added || !PaySurchargeRules.ZONE.equals(rule))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!postcodeOn && PaySurchargeRules.POSTCODE.equals(rule))
|
||||
{
|
||||
result.getSkipped().add(blank(fee.getFeeName()) + ":未打开命中邮编");
|
||||
continue;
|
||||
}
|
||||
if (!zoneOn && PaySurchargeRules.ZONE.equals(rule))
|
||||
{
|
||||
result.getSkipped().add(blank(fee.getFeeName()) + ":未打开命中分区");
|
||||
continue;
|
||||
}
|
||||
boolean postcodeDriven = PaySurchargeRules.POSTCODE.equals(rule) || !blank(fee.getPostcodeParam()).isEmpty();
|
||||
@@ -346,9 +373,9 @@ public final class PayCostCalculator
|
||||
{
|
||||
result.getSkipped().add(name + ":订单没有长宽高");
|
||||
}
|
||||
else if (PaySurchargeRules.matchesSize(fee.getCalcParam(), length, width, height))
|
||||
else if (sizeHit(product, fee, length, width, height, weight))
|
||||
{
|
||||
PayCostLine matched = matchedFee(fee, freight, currency, "尺寸命中 " + fee.getCalcParam());
|
||||
PayCostLine matched = matchedFee(fee, freight, currency, "尺寸命中");
|
||||
if (matched != null)
|
||||
{
|
||||
sized.add(matched);
|
||||
@@ -384,6 +411,10 @@ public final class PayCostCalculator
|
||||
}
|
||||
else if (PaySurchargeRules.ALWAYS.equals(rule))
|
||||
{
|
||||
if (!PaySurchargeRules.allowsAlways(ruleDetails(product, PaySurchargeRules.ALWAYS), name, bizDate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PayCostLine matched = matchedFee(fee, freight, currency, blank(fee.getChargeText()));
|
||||
if (matched != null)
|
||||
{
|
||||
@@ -517,6 +548,42 @@ public final class PayCostCalculator
|
||||
return top;
|
||||
}
|
||||
|
||||
private static boolean sizeHit(PayProduct product, PaySurcharge fee, BigDecimal length, BigDecimal width, BigDecimal height,
|
||||
BigDecimal weight)
|
||||
{
|
||||
List<Map<String, Object>> details = ruleDetails(product, PaySurchargeRules.SIZE);
|
||||
if (details.isEmpty())
|
||||
{
|
||||
return PaySurchargeRules.matchesSize(fee.getCalcParam(), length, width, height);
|
||||
}
|
||||
for (Map<String, Object> detail : details)
|
||||
{
|
||||
if (PaySurchargeRules.matchesConfiguredSize(detail, length, width, height, weight))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<Map<String, Object>> ruleDetails(PayProduct product, String calcRule)
|
||||
{
|
||||
List<Map<String, Object>> details = new ArrayList<>();
|
||||
if (product == null || product.getBillingRules() == null)
|
||||
{
|
||||
return details;
|
||||
}
|
||||
for (PaySurchargeRule rule : product.getBillingRules())
|
||||
{
|
||||
if (rule == null || !calcRule.equals(rule.getCalcRule()) || "0".equals(rule.getEnabled()) || rule.getDetail() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
details.add(rule.getDetail());
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
private static boolean remoteFee(PaySurcharge fee)
|
||||
{
|
||||
String name = blank(fee.getFeeName()).toLowerCase(Locale.ROOT);
|
||||
@@ -540,7 +607,7 @@ public final class PayCostCalculator
|
||||
continue;
|
||||
}
|
||||
boolean areaKnown = !blank(hit.getZoneName()).isEmpty() || !blank(hit.getRegionName()).isEmpty();
|
||||
if (zoned && areaKnown && !sameArea(hit, fee))
|
||||
if (zoned && (!areaKnown || !sameArea(hit, fee)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ public final class PayRemoteBook
|
||||
List<Hit> hits = new ArrayList<>();
|
||||
for (PayPostcodeLine line : lines)
|
||||
{
|
||||
if (line == null || !remote.containsKey(line.getGroupId()) || !PayZoneMatcher.matchesLine(line, postcode))
|
||||
if (line == null || !remote.containsKey(line.getGroupId()) || !sameLineCountry(line, countryCode, countryName)
|
||||
|| !PayZoneMatcher.matchesLine(line, postcode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -146,6 +147,21 @@ public final class PayRemoteBook
|
||||
return new BigDecimal(matcher.group(1));
|
||||
}
|
||||
|
||||
/** 明细上写了国家时必须和订单目的国一致。没写则沿用分组上的国家。 */
|
||||
private static boolean sameLineCountry(PayPostcodeLine line, String countryCode, String countryName)
|
||||
{
|
||||
boolean lineBlank = blank(line.getCountryCode()) && blank(line.getCountryName());
|
||||
if (lineBlank)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!blank(countryCode) && countryCode.equalsIgnoreCase(line.getCountryCode() == null ? "" : line.getCountryCode().trim()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return !blank(countryName) && countryName.trim().equals(line.getCountryName() == null ? "" : line.getCountryName().trim());
|
||||
}
|
||||
|
||||
private static boolean sameCountry(PayPostcodeGroup group, String countryCode, String countryName)
|
||||
{
|
||||
boolean orderBlank = blank(countryCode) && blank(countryName);
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.bms.price.pay;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.customer.supplierinfo.domain.BmsSupplier;
|
||||
import com.bms.customer.supplierinfo.mapper.BmsSupplierMapper;
|
||||
import com.bms.price.mapper.PayFinanceMapper;
|
||||
|
||||
/** 把价卡上的归属名称对到供应商主数据。 */
|
||||
@Component
|
||||
public class PaySettleSupport
|
||||
{
|
||||
@Autowired
|
||||
private BmsSupplierMapper supplierMapper;
|
||||
|
||||
@Autowired
|
||||
private PayFinanceMapper payFinanceMapper;
|
||||
|
||||
/**
|
||||
* 计算费用落库后,按价卡归属写上结算供应商。对不上供应商时保持为空。
|
||||
*
|
||||
* @param billId 计费订单ID
|
||||
* @param cardName 价卡归属名称
|
||||
*/
|
||||
public void stampCalc(String billId, String cardName)
|
||||
{
|
||||
BmsSupplier supplier = match(cardName);
|
||||
if (supplier == null || billId == null || billId.isBlank())
|
||||
{
|
||||
return;
|
||||
}
|
||||
payFinanceMapper.stampCalc(billId, supplier.getSupplierId(), supplier.getSupplierName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 用价卡归属名称找到唯一供应商。名称、编码或对外代码一致优先,其次互相包含。
|
||||
*
|
||||
* @param cardName 价卡上的供应商名称
|
||||
* @return 对上的供应商;对不上或对上多家且没有精确项时返回 null
|
||||
*/
|
||||
public BmsSupplier match(String cardName)
|
||||
{
|
||||
String card = norm(cardName);
|
||||
if (card.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<BmsSupplier> all = supplierMapper.selectBmsSupplierList(new BmsSupplier());
|
||||
BmsSupplier exact = null;
|
||||
BmsSupplier fuzzy = null;
|
||||
int fuzzyCount = 0;
|
||||
if (all == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (BmsSupplier supplier : all)
|
||||
{
|
||||
if (supplier == null || supplier.getSupplierId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String name = norm(supplier.getSupplierName());
|
||||
String code = norm(supplier.getSupplierCode());
|
||||
String dw = norm(supplier.getSupplierCodeDw());
|
||||
if (card.equals(name) || card.equals(code) || (!dw.isEmpty() && card.equals(dw)))
|
||||
{
|
||||
exact = supplier;
|
||||
break;
|
||||
}
|
||||
if (card.length() >= 2 && name.length() >= 2 && (name.contains(card) || card.contains(name)))
|
||||
{
|
||||
fuzzy = supplier;
|
||||
fuzzyCount++;
|
||||
}
|
||||
}
|
||||
if (exact != null)
|
||||
{
|
||||
return exact;
|
||||
}
|
||||
return fuzzyCount == 1 ? fuzzy : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所选供应商和价卡归属是不是同一家。
|
||||
*
|
||||
* @param supplier 账单上选择的供应商
|
||||
* @param cardName 价卡归属名称
|
||||
* @return 对得上时为 true
|
||||
*/
|
||||
public boolean same(BmsSupplier supplier, String cardName)
|
||||
{
|
||||
if (supplier == null || supplier.getSupplierId() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BmsSupplier matched = match(cardName);
|
||||
return matched != null && supplier.getSupplierId().equals(matched.getSupplierId());
|
||||
}
|
||||
|
||||
private static String norm(String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return value.replaceAll("\\s+", "").toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
@@ -125,13 +125,23 @@ public final class PaySurchargeRules
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String compact = value.replaceAll("[^A-Z0-9]", "");
|
||||
String whole = postcode.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]", "");
|
||||
if (!compact.isEmpty() && compact.equals(whole))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsed != null && tokenMatches(value, parsed))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsed == null && value.matches("\\d{1,5}") && !digits.isEmpty() && digits.startsWith(value))
|
||||
if (parsed == null && value.matches("\\d{1,5}") && !digits.isEmpty())
|
||||
{
|
||||
return true;
|
||||
boolean full = value.length() >= 4 ? digits.equals(value) : digits.startsWith(value);
|
||||
if (full)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -159,6 +169,218 @@ public final class PaySurchargeRules
|
||||
return String.join(",", tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按附加费配置里的超尺寸明细判断。上限表示超过这个值才命中。箱规按从大到小比,任一边超过对应边即命中。
|
||||
*/
|
||||
public static boolean matchesConfiguredSize(java.util.Map<String, Object> detail, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, BigDecimal weight)
|
||||
{
|
||||
if (detail == null || detail.isEmpty() || length == null || width == null || height == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BigDecimal weightMin = number(detail.get("weightMin"));
|
||||
BigDecimal weightMax = number(detail.get("weightMax"));
|
||||
if (weightMin != null || weightMax != null)
|
||||
{
|
||||
if (weight == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (weightMin != null && weight.compareTo(weightMin) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (weightMax != null && weight.compareTo(weightMax) > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BigDecimal[] sides = desc(length, width, height);
|
||||
List<Boolean> checks = new ArrayList<>();
|
||||
addBound(checks, sides[0], detail.get("longestMin"), detail.get("longestMax"));
|
||||
addBound(checks, sides[1], detail.get("secondMin"), detail.get("secondMax"));
|
||||
addBound(checks, length.add(width).add(height), detail.get("sumMin"), detail.get("sumMax"));
|
||||
addBound(checks, sides[0].add(sides[1].add(sides[2]).multiply(new BigDecimal("2"))), detail.get("girthMin"), detail.get("girthMax"));
|
||||
BigDecimal volumeMin = number(detail.get("volumeMin"));
|
||||
BigDecimal volumeMax = number(detail.get("volumeMax"));
|
||||
if (volumeMin != null || volumeMax != null)
|
||||
{
|
||||
BigDecimal cubic = length.multiply(width).multiply(height).divide(new BigDecimal("1000000"), 6, RoundingMode.HALF_UP);
|
||||
addBound(checks, cubic, volumeMin, volumeMax);
|
||||
}
|
||||
BigDecimal cubicMax = number(detail.get("cubicMax"));
|
||||
if (cubicMax != null)
|
||||
{
|
||||
checks.add(length.multiply(width).multiply(height).compareTo(cubicMax) > 0);
|
||||
}
|
||||
BigDecimal boxLength = number(detail.get("boxLength"));
|
||||
BigDecimal boxWidth = number(detail.get("boxWidth"));
|
||||
BigDecimal boxHeight = number(detail.get("boxHeight"));
|
||||
if (boxLength != null && boxWidth != null && boxHeight != null)
|
||||
{
|
||||
boolean under = "UNDER".equals(String.valueOf(detail.get("boxMode")));
|
||||
checks.add(under ? withinBox(sides, boxLength, boxWidth, boxHeight) : exceedsBox(sides, boxLength, boxWidth, boxHeight));
|
||||
}
|
||||
if (checks.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ("ALL".equals(String.valueOf(detail.get("matchMode"))))
|
||||
{
|
||||
return !checks.contains(Boolean.FALSE);
|
||||
}
|
||||
return checks.contains(Boolean.TRUE);
|
||||
}
|
||||
|
||||
/** 每票都收。写了费用名的规则只约束这一项;带生效月日的规则只在这段日期内收取。 */
|
||||
public static boolean allowsAlways(List<java.util.Map<String, Object>> details, String feeName, java.util.Date bizDate)
|
||||
{
|
||||
if (details == null || details.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
String name = feeName == null ? "" : feeName.trim();
|
||||
List<java.util.Map<String, Object>> named = new ArrayList<>();
|
||||
List<java.util.Map<String, Object>> shared = new ArrayList<>();
|
||||
for (java.util.Map<String, Object> detail : details)
|
||||
{
|
||||
if (detail == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String ruleName = detail.get("feeName") == null ? "" : String.valueOf(detail.get("feeName")).trim();
|
||||
if (ruleName.isEmpty())
|
||||
{
|
||||
shared.add(detail);
|
||||
}
|
||||
else if (ruleName.equals(name))
|
||||
{
|
||||
named.add(detail);
|
||||
}
|
||||
}
|
||||
List<java.util.Map<String, Object>> mine = named.isEmpty() ? shared : named;
|
||||
if (mine.isEmpty())
|
||||
{
|
||||
return shared.isEmpty();
|
||||
}
|
||||
java.time.LocalDate day = bizDate == null ? null : bizDate.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
|
||||
for (java.util.Map<String, Object> detail : mine)
|
||||
{
|
||||
if (!hasPeriod(detail))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (day != null && inPeriod(day, detail))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addBound(List<Boolean> checks, BigDecimal actual, Object min, Object max)
|
||||
{
|
||||
BigDecimal lower = min instanceof BigDecimal ? (BigDecimal) min : number(min);
|
||||
BigDecimal upper = max instanceof BigDecimal ? (BigDecimal) max : number(max);
|
||||
if (lower == null && upper == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (lower != null && upper != null)
|
||||
{
|
||||
checks.add(actual.compareTo(lower) > 0 && actual.compareTo(upper) <= 0);
|
||||
}
|
||||
else if (upper != null)
|
||||
{
|
||||
checks.add(actual.compareTo(upper) > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
checks.add(actual.compareTo(lower) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean withinBox(BigDecimal[] parcel, BigDecimal a, BigDecimal b, BigDecimal c)
|
||||
{
|
||||
BigDecimal[] limit = desc(a, b, c);
|
||||
return parcel[0].compareTo(limit[0]) < 0 && parcel[1].compareTo(limit[1]) < 0 && parcel[2].compareTo(limit[2]) < 0;
|
||||
}
|
||||
|
||||
private static boolean hasPeriod(java.util.Map<String, Object> detail)
|
||||
{
|
||||
return text(detail.get("periodStart")) != null || text(detail.get("periodEnd")) != null;
|
||||
}
|
||||
|
||||
private static boolean inPeriod(java.time.LocalDate day, java.util.Map<String, Object> detail)
|
||||
{
|
||||
int[] start = monthDay(text(detail.get("periodStart")));
|
||||
int[] end = monthDay(text(detail.get("periodEnd")));
|
||||
int current = day.getMonthValue() * 100 + day.getDayOfMonth();
|
||||
int from = start == null ? 101 : start[0] * 100 + start[1];
|
||||
int to = end == null ? 1231 : end[0] * 100 + end[1];
|
||||
if (from <= to)
|
||||
{
|
||||
return current >= from && current <= to;
|
||||
}
|
||||
return current >= from || current <= to;
|
||||
}
|
||||
|
||||
private static int[] monthDay(String text)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String[] parts = text.split("-");
|
||||
try
|
||||
{
|
||||
if (parts.length == 2)
|
||||
{
|
||||
return new int[] { Integer.parseInt(parts[0]), Integer.parseInt(parts[1]) };
|
||||
}
|
||||
if (parts.length == 3)
|
||||
{
|
||||
return new int[] { Integer.parseInt(parts[1]), Integer.parseInt(parts[2]) };
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String text(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
|
||||
private static BigDecimal number(Object value)
|
||||
{
|
||||
if (value == null || (value instanceof String text && text.isBlank()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (value instanceof BigDecimal decimal)
|
||||
{
|
||||
return decimal;
|
||||
}
|
||||
try
|
||||
{
|
||||
return new BigDecimal(String.valueOf(value).trim());
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matchesWeight(String param, BigDecimal weight)
|
||||
{
|
||||
if (param == null || param.isBlank() || weight == null)
|
||||
|
||||
@@ -247,11 +247,56 @@ public final class PayZoneMatcher
|
||||
private static int matchedLength(PayPostcodeLine line, String postcode)
|
||||
{
|
||||
int best = -1;
|
||||
best = longer(best, line.getPostcode(), postcode);
|
||||
best = longer(best, line.getPostcodeEnd(), postcode);
|
||||
String start = line.getPostcode();
|
||||
String end = line.getPostcodeEnd();
|
||||
if (start != null && end != null && !end.isBlank() && !compact(start).equals(compact(end)))
|
||||
{
|
||||
best = Math.max(best, rangeLength(start, end, postcode));
|
||||
}
|
||||
best = longer(best, start, postcode);
|
||||
return best;
|
||||
}
|
||||
|
||||
/** 完整邮编按整段相等,短码按前缀。区间用起止邮编,含两端。 */
|
||||
private static int rangeLength(String start, String end, String postcode)
|
||||
{
|
||||
String from = compact(start);
|
||||
String to = compact(end);
|
||||
String value = compact(postcode);
|
||||
if (from.isEmpty() || to.isEmpty() || value.isEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (from.compareTo(to) > 0)
|
||||
{
|
||||
String swap = from;
|
||||
from = to;
|
||||
to = swap;
|
||||
}
|
||||
if (digits(from) && digits(to) && digits(value) && value.length() == from.length())
|
||||
{
|
||||
long low = Long.parseLong(from);
|
||||
long high = Long.parseLong(to);
|
||||
long current = Long.parseLong(value);
|
||||
return current >= low && current <= high ? from.length() : -1;
|
||||
}
|
||||
if (from.length() == to.length() && value.length() == from.length() && value.compareTo(from) >= 0 && value.compareTo(to) <= 0)
|
||||
{
|
||||
return from.length();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static String compact(String value)
|
||||
{
|
||||
return value == null ? "" : value.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]", "");
|
||||
}
|
||||
|
||||
private static boolean digits(String value)
|
||||
{
|
||||
return !value.isEmpty() && value.chars().allMatch(Character::isDigit);
|
||||
}
|
||||
|
||||
private static int longer(int best, String raw, String postcode)
|
||||
{
|
||||
if (raw == null || raw.isBlank())
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.price.domain.PayBill;
|
||||
import com.bms.price.domain.PayBillCommand;
|
||||
import com.bms.price.domain.PayBillFile;
|
||||
import com.bms.price.domain.PayBillPreview;
|
||||
|
||||
/**
|
||||
@@ -66,4 +69,43 @@ public interface IPayBillService
|
||||
* @return 成功时为 1
|
||||
*/
|
||||
int voidBill(PayBillCommand command);
|
||||
|
||||
/** 草稿提交审核。 */
|
||||
int submit(Long billId);
|
||||
|
||||
/** 审核通过。蓝字需要已有凭证。红字会冲回原账单并释放费用。 */
|
||||
int approve(Long billId);
|
||||
|
||||
/** 审核驳回,回到草稿。 */
|
||||
int reject(PayBillCommand command);
|
||||
|
||||
/** 草稿登记对方金额。 */
|
||||
int saveSupplierAmounts(PayBillCommand command);
|
||||
|
||||
/** 接受我方或接受对方。接受对方会另写调整费用。 */
|
||||
int acceptDiff(PayBillCommand command);
|
||||
|
||||
/** 从草稿剔除一条费用。 */
|
||||
int removeLine(PayBillCommand command);
|
||||
|
||||
/** 上传应付凭证。 */
|
||||
int saveFile(PayBillFile file);
|
||||
|
||||
/** 删除草稿或待审核账单上的凭证。 */
|
||||
int deleteFile(Long fileId);
|
||||
|
||||
/** 查询应付凭证。 */
|
||||
List<PayBillFile> listFiles(PayBillFile query);
|
||||
|
||||
/** 对未付款的已确认账单开红字草稿。 */
|
||||
PayBill redBill(PayBillCommand command);
|
||||
|
||||
/** 草稿保存税率和汇率,并重算不含税、税额、本位币。 */
|
||||
int saveFinance(PayBillCommand command);
|
||||
|
||||
/** 导出对账单,供供应商回填对方金额。 */
|
||||
void exportStatement(Long billId, OutputStream out);
|
||||
|
||||
/** 按行号导入对方金额。 */
|
||||
int importSupplierAmounts(Long billId, MultipartFile file);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,20 @@ public interface IPayOrderCostService
|
||||
* @return 本次算出的产品、版本、币种和费用明细
|
||||
*/
|
||||
PayOrderCostResult generate(String billId);
|
||||
|
||||
/**
|
||||
* 单独提交一票尾程。失败时只回滚这一票,不影响外面的对账单事务。
|
||||
*
|
||||
* @param billId 计费订单ID
|
||||
* @return 本次算出的产品、版本、币种和费用明细
|
||||
*/
|
||||
PayOrderCostResult generateIsolated(String billId);
|
||||
|
||||
/**
|
||||
* 这票订单当前尾程价卡上的归属供应商名称。没有价卡时返回空字符串。
|
||||
*
|
||||
* @param billId 计费订单ID
|
||||
* @return 价卡归属名称
|
||||
*/
|
||||
String cardSupplierName(String billId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.price.domain.PayPayment;
|
||||
import com.bms.price.domain.PayPaymentAlloc;
|
||||
|
||||
/** 应付付款。草稿保存核销计划,确认后才回写账单已付金额。 */
|
||||
public interface IPayPaymentService
|
||||
{
|
||||
List<PayPayment> selectPayPaymentList(PayPayment query);
|
||||
|
||||
PayPayment selectPayPaymentDetail(Long paymentId);
|
||||
|
||||
List<PayPaymentAlloc> selectOpenBills(Long supplierId, String currency);
|
||||
|
||||
PayPayment create(PayPayment payment);
|
||||
|
||||
int confirm(Long paymentId);
|
||||
|
||||
int voidPayment(Long paymentId);
|
||||
|
||||
/** 反核销已确认付款。账单未付金额回来。 */
|
||||
int reverse(Long paymentId);
|
||||
|
||||
/** 供应商退回这笔付款。核销冲回。 */
|
||||
int refund(Long paymentId);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.price.domain.PaySurchargeRule;
|
||||
import com.bms.price.domain.PaySurchargeRuleSave;
|
||||
|
||||
/** 应付附加费计费条件。一个产品版本只保留一份。 */
|
||||
public interface IPaySurchargeRuleService
|
||||
{
|
||||
List<PaySurchargeRule> list(Long productId, Long termId);
|
||||
|
||||
/** 上一版的条件,只读出来供当前页编辑,不直接落库 */
|
||||
List<PaySurchargeRule> previous(Long productId);
|
||||
|
||||
/** 覆盖所选产品当前版本的配置。同一版本不会留下两份。 */
|
||||
void save(PaySurchargeRuleSave command);
|
||||
}
|
||||
+947
-45
File diff suppressed because it is too large
Load Diff
+80
-3
@@ -5,6 +5,7 @@ import java.util.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
@@ -17,6 +18,7 @@ import com.bms.price.domain.PayPostcodeGroup;
|
||||
import com.bms.price.domain.PayPostcodeLine;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
import com.bms.price.domain.PaySurcharge;
|
||||
import com.bms.price.domain.PaySurchargeRule;
|
||||
import com.bms.price.domain.PayWeightBand;
|
||||
import com.bms.price.domain.PrcOrderBill;
|
||||
import com.bms.price.domain.PrcOrderBillExt;
|
||||
@@ -28,11 +30,13 @@ import com.bms.price.mapper.PrcOrderBillMapper;
|
||||
import com.bms.price.mapper.PrcPayBillFeeMapper;
|
||||
import com.bms.price.mapper.PrcPayBillMapper;
|
||||
import com.bms.price.pay.PayCostCalculator;
|
||||
import com.bms.price.pay.PaySettleSupport;
|
||||
import com.bms.price.pay.PayRemoteBook;
|
||||
import com.bms.price.pay.PaySurchargeRules;
|
||||
import com.bms.price.pay.PayZoneMatcher;
|
||||
import com.bms.price.service.IPayOrderCostService;
|
||||
import com.bms.price.service.IPayProductService;
|
||||
import com.bms.price.service.IPaySurchargeRuleService;
|
||||
import com.bms.price.service.IPrcPayBillFeeService;
|
||||
|
||||
/**
|
||||
@@ -62,9 +66,15 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService
|
||||
@Autowired
|
||||
private IPrcPayBillFeeService payBillFeeService;
|
||||
|
||||
@Autowired
|
||||
private PaySettleSupport paySettleSupport;
|
||||
|
||||
@Autowired
|
||||
private PayPostcodeMapper payPostcodeMapper;
|
||||
|
||||
@Autowired
|
||||
private IPaySurchargeRuleService paySurchargeRuleService;
|
||||
|
||||
/**
|
||||
* 重算一票订单的尾程应付并落库。
|
||||
* 先找渠道和计费日对应的价卡版本,再用重量、尺寸、邮编算出费用,写回订单应付。
|
||||
@@ -72,6 +82,13 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService
|
||||
* @param billId 计费订单ID
|
||||
* @return 本次使用的渠道、价卡版本和费用明细
|
||||
*/
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PayOrderCostResult generateIsolated(String billId)
|
||||
{
|
||||
return generate(billId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayOrderCostResult generate(String billId)
|
||||
@@ -93,10 +110,21 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService
|
||||
Integer orderStatus = bill.getOrderStatus() != null ? bill.getOrderStatus() : (ext == null ? null : ext.getOrderStatus());
|
||||
List<PayPostcodeGroup> linked = linkedGroups(product);
|
||||
String zoneName = resolveZone(channel, product, bill.getDestPostcode(), bill.getCountryCode(), bill.getCountryName(), linked);
|
||||
// 根据邮编判断是否是偏远地区
|
||||
List<PayRemoteBook.Hit> remoteHits = remoteHits(linked, bill.getDestPostcode(), bill.getCountryCode(), bill.getCountryName());
|
||||
List<PaySurchargeRule> billingRules = billingRules(product);
|
||||
product.setBillingRules(billingRules);
|
||||
boolean postcodeOn = ruleOn(billingRules, PaySurchargeRules.POSTCODE);
|
||||
boolean zoneOn = ruleOn(billingRules, PaySurchargeRules.ZONE);
|
||||
List<PayRemoteBook.Hit> remoteHits = null;
|
||||
if (postcodeOn)
|
||||
{
|
||||
remoteHits = remoteHits(linked, bill.getDestPostcode(), bill.getCountryCode(), bill.getCountryName());
|
||||
if (remoteHits == null)
|
||||
{
|
||||
remoteHits = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
PayOrderCostResult result = PayCostCalculator.calculate(product, weight, bill.getPkgLength(), bill.getPkgWidth(),
|
||||
bill.getPkgHeight(), bill.getDestPostcode(), zoneName, bizDate, orderStatus, remoteHits);
|
||||
bill.getPkgHeight(), bill.getDestPostcode(), zoneName, bizDate, orderStatus, remoteHits, postcodeOn, zoneOn);
|
||||
result.setBillId(bill.getBillId());
|
||||
result.setChannelId(channel.getId());
|
||||
result.setChannelName(channel.getChannelName());
|
||||
@@ -112,15 +140,64 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService
|
||||
String end = product.getTermEnd() == null ? "不限" : day(product.getTermEnd());
|
||||
result.setMessage("渠道「" + channelName + "」产品编号 " + product.getOriginId() + " 第 " + product.getVersionNo()
|
||||
+ " 版(" + day(product.getTermStart()) + " 至 " + end + ")," + result.getMessage());
|
||||
paySettleSupport.stampCalc(bill.getBillId(), product.getSupplierName());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cardSupplierName(String billId)
|
||||
{
|
||||
if (billId == null || billId.isBlank())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
try
|
||||
{
|
||||
PrcOrderBill bill = orderBillMapper.selectPrcOrderBillById(billId.trim());
|
||||
if (bill == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
PrcOrderBillExt ext = orderBillExtMapper.selectByBillId(bill.getBillId());
|
||||
ServiceChannel channel = resolveChannel(ext);
|
||||
Date bizDate = bill.getBizDate() != null ? bill.getBizDate() : bill.getOrderTime();
|
||||
PayProduct product = payProductService.selectPriceForDate(channel.getId(), bizDate);
|
||||
return product.getSupplierName() == null ? "" : product.getSupplierName().trim();
|
||||
}
|
||||
catch (ServiceException ex)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用产品名称、报价工作表、字典值和关联分区表,对上这份产品的邮编配置。
|
||||
*
|
||||
* @param product 当前价卡版本
|
||||
* @return 名称能对上的邮编组,没有时为空列表
|
||||
*/
|
||||
private List<PaySurchargeRule> billingRules(PayProduct product)
|
||||
{
|
||||
if (product == null || product.getProductId() == null || product.getTermId() == null)
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<PaySurchargeRule> rules = paySurchargeRuleService.list(product.getProductId(), product.getTermId());
|
||||
return rules == null ? new ArrayList<>() : rules;
|
||||
}
|
||||
|
||||
private static boolean ruleOn(List<PaySurchargeRule> rules, String calcRule)
|
||||
{
|
||||
for (PaySurchargeRule rule : rules)
|
||||
{
|
||||
if (rule != null && calcRule.equals(rule.getCalcRule()) && !"0".equals(rule.getEnabled()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<PayPostcodeGroup> linkedGroups(PayProduct product)
|
||||
{
|
||||
List<String> names = new ArrayList<>();
|
||||
|
||||
+413
@@ -0,0 +1,413 @@
|
||||
package com.bms.price.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.bms.common.enums.PaymentMesEnum;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.customer.supplierinfo.domain.BmsSupplier;
|
||||
import com.bms.customer.supplierinfo.mapper.BmsSupplierMapper;
|
||||
import com.bms.price.domain.PayBill;
|
||||
import com.bms.price.domain.PayBillLog;
|
||||
import com.bms.price.domain.PayPayment;
|
||||
import com.bms.price.domain.PayPaymentAlloc;
|
||||
import com.bms.price.mapper.PayBillMapper;
|
||||
import com.bms.price.mapper.PayPaymentMapper;
|
||||
import com.bms.price.service.IPayPaymentService;
|
||||
|
||||
/** 应付付款。确认时按核销金额回写账单,一张付款可以核多张账单。 */
|
||||
@Service
|
||||
public class PayPaymentServiceImpl implements IPayPaymentService
|
||||
{
|
||||
@Autowired
|
||||
private PayPaymentMapper payPaymentMapper;
|
||||
|
||||
@Autowired
|
||||
private PayBillMapper payBillMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsSupplierMapper supplierMapper;
|
||||
|
||||
@Override
|
||||
public List<PayPayment> selectPayPaymentList(PayPayment query)
|
||||
{
|
||||
return payPaymentMapper.selectPayPaymentList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayPayment selectPayPaymentDetail(Long paymentId)
|
||||
{
|
||||
PayPayment payment = required(paymentId);
|
||||
payment.setAllocs(payPaymentMapper.selectAllocs(paymentId));
|
||||
return payment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PayPaymentAlloc> selectOpenBills(Long supplierId, String currency)
|
||||
{
|
||||
if (supplierId == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PICK_SUP.exception();
|
||||
}
|
||||
if (currency == null || currency.isBlank())
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_CURRENCY.exception();
|
||||
}
|
||||
return payBillMapper.selectOpenBills(supplierId, currency.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayPayment create(PayPayment payment)
|
||||
{
|
||||
if (payment == null || payment.getSupplierId() == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PICK_SUP.exception();
|
||||
}
|
||||
if (payment.getAmount() == null || payment.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_AMT.exception();
|
||||
}
|
||||
if (payment.getPayDate() == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_DATE.exception();
|
||||
}
|
||||
if (payment.getCurrency() == null || payment.getCurrency().isBlank())
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_CURRENCY.exception();
|
||||
}
|
||||
BmsSupplier supplier = supplierMapper.selectBmsSupplierBySupplierId(payment.getSupplierId());
|
||||
if (supplier == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_SUP_MISS.exception();
|
||||
}
|
||||
String operator = SecurityUtils.getUsername();
|
||||
payment.setPaymentNo(nextNo());
|
||||
payment.setSupplierName(supplier.getSupplierName());
|
||||
payment.setCurrency(payment.getCurrency().trim());
|
||||
payment.setPayMethod(payment.getPayMethod() == null || payment.getPayMethod().isBlank() ? "BANK" : payment.getPayMethod());
|
||||
payment.setBankFee(payment.getBankFee() == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : payment.getBankFee());
|
||||
payment.setAmount(payment.getAmount().setScale(2, RoundingMode.HALF_UP));
|
||||
if (payment.getExchangeRate() == null || payment.getExchangeRate().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
payment.setExchangeRate(BigDecimal.ONE);
|
||||
}
|
||||
payment.setCreateBy(operator);
|
||||
checkAllocs(payment);
|
||||
payPaymentMapper.insertPayPayment(payment);
|
||||
if (payment.getAllocs() != null)
|
||||
{
|
||||
for (PayPaymentAlloc alloc : payment.getAllocs())
|
||||
{
|
||||
if (alloc.getBillId() == null || alloc.getAllocAmount() == null || alloc.getAllocAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
alloc.setPaymentId(payment.getPaymentId());
|
||||
alloc.setAllocAmount(alloc.getAllocAmount().setScale(2, RoundingMode.HALF_UP));
|
||||
payPaymentMapper.insertAlloc(alloc);
|
||||
}
|
||||
}
|
||||
return selectPayPaymentDetail(payment.getPaymentId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int confirm(Long paymentId)
|
||||
{
|
||||
PayPayment payment = required(paymentId);
|
||||
if (!"DRAFT".equals(payment.getStatus()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_DRAFT.exception();
|
||||
}
|
||||
payment.setAllocs(payPaymentMapper.selectAllocs(paymentId));
|
||||
checkAllocs(payment);
|
||||
String operator = SecurityUtils.getUsername();
|
||||
if (operator.equals(payment.getCreateBy()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_SAME_PAY.exception();
|
||||
}
|
||||
BigDecimal rate = payment.getExchangeRate() == null ? BigDecimal.ONE : payment.getExchangeRate();
|
||||
BigDecimal allocSum = BigDecimal.ZERO;
|
||||
if (payment.getAllocs() != null)
|
||||
{
|
||||
for (PayPaymentAlloc alloc : payment.getAllocs())
|
||||
{
|
||||
if (alloc.getAllocAmount() == null || alloc.getAllocAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PayBill bill = payBillMapper.selectPayBillById(alloc.getBillId());
|
||||
applyAlloc(payment, bill, alloc.getAllocAmount(), operator);
|
||||
allocSum = allocSum.add(alloc.getAllocAmount());
|
||||
}
|
||||
}
|
||||
payment.setExchangeRate(rate);
|
||||
payment.setAllocAmount(allocSum.setScale(2, RoundingMode.HALF_UP));
|
||||
payment.setBaseAmount(payment.getAmount().multiply(rate).setScale(2, RoundingMode.HALF_UP));
|
||||
payment.setStatus("PAID");
|
||||
payment.setConfirmBy(operator);
|
||||
payment.setConfirmTime(new Date());
|
||||
payPaymentMapper.updatePayPaymentStatus(payment);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int reverse(Long paymentId)
|
||||
{
|
||||
PayPayment payment = required(paymentId);
|
||||
if (!"PAID".equals(payment.getStatus()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_NO_REVERSE.exception();
|
||||
}
|
||||
String operator = SecurityUtils.getUsername();
|
||||
if (operator.equals(payment.getConfirmBy()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_SAME_PAY.exception();
|
||||
}
|
||||
BigDecimal back = releaseAllocs(payment, operator);
|
||||
if (back.compareTo(BigDecimal.ZERO) == 0)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_NO_BACK.exception();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int refund(Long paymentId)
|
||||
{
|
||||
PayPayment payment = required(paymentId);
|
||||
if (!"PAID".equals(payment.getStatus()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_NO_REVERSE.exception();
|
||||
}
|
||||
String operator = SecurityUtils.getUsername();
|
||||
if (operator.equals(payment.getConfirmBy()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_SAME_PAY.exception();
|
||||
}
|
||||
BigDecimal back = releaseAllocs(payment, operator);
|
||||
if (back.compareTo(BigDecimal.ZERO) == 0)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_NO_BACK.exception();
|
||||
}
|
||||
payment.setAllocAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
payment.setStatus("REFUNDED");
|
||||
payPaymentMapper.updatePayPaymentStatus(payment);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int voidPayment(Long paymentId)
|
||||
{
|
||||
PayPayment payment = required(paymentId);
|
||||
if (!"DRAFT".equals(payment.getStatus()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_DRAFT.exception();
|
||||
}
|
||||
payment.setStatus("VOID");
|
||||
payPaymentMapper.updatePayPaymentStatus(payment);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void checkAllocs(PayPayment payment)
|
||||
{
|
||||
BigDecimal sum = BigDecimal.ZERO;
|
||||
Set<Long> seen = new HashSet<>();
|
||||
if (payment.getAllocs() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (PayPaymentAlloc alloc : payment.getAllocs())
|
||||
{
|
||||
if (alloc.getBillId() == null || alloc.getAllocAmount() == null || alloc.getAllocAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!seen.add(alloc.getBillId()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_ALLOC_DUP.exception();
|
||||
}
|
||||
PayBill bill = payBillMapper.selectPayBillById(alloc.getBillId());
|
||||
if (bill == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_BILL_MISS.exception();
|
||||
}
|
||||
if (!payment.getSupplierId().equals(bill.getSupplierId()) || !payment.getCurrency().equals(bill.getCurrency()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_SAME.exception();
|
||||
}
|
||||
if (!"CONFIRMED".equals(bill.getStatus()) && !"PARTIAL".equals(bill.getStatus()))
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_BILL.exception();
|
||||
}
|
||||
BigDecimal open = openAmount(bill);
|
||||
if (alloc.getAllocAmount().compareTo(open) > 0)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_ALLOC_AMT.exception();
|
||||
}
|
||||
sum = sum.add(alloc.getAllocAmount());
|
||||
}
|
||||
if (sum.compareTo(payment.getAmount()) > 0)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_ALLOC_OVER.exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲回仍有效的核销,并减少付款单上的已核销金额。
|
||||
*
|
||||
* @param payment 已付款单
|
||||
* @param operator 操作人
|
||||
* @return 冲回的金额
|
||||
*/
|
||||
private BigDecimal releaseAllocs(PayPayment payment, String operator)
|
||||
{
|
||||
List<PayPaymentAlloc> allocs = payPaymentMapper.selectAllocs(payment.getPaymentId());
|
||||
BigDecimal back = BigDecimal.ZERO;
|
||||
for (PayPaymentAlloc alloc : allocs)
|
||||
{
|
||||
if (alloc.getAllocAmount() == null || !"ACTIVE".equals(alloc.getAllocStatus()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PayBill bill = payBillMapper.selectPayBillById(alloc.getBillId());
|
||||
if (bill != null)
|
||||
{
|
||||
undoAlloc(payment, bill, alloc.getAllocAmount(), operator);
|
||||
}
|
||||
payPaymentMapper.reverseAlloc(alloc.getAllocId(), operator);
|
||||
back = back.add(alloc.getAllocAmount());
|
||||
}
|
||||
back = back.setScale(2, RoundingMode.HALF_UP);
|
||||
if (back.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
BigDecimal allocAmount = payment.getAllocAmount() == null ? BigDecimal.ZERO : payment.getAllocAmount();
|
||||
BigDecimal nextAlloc = allocAmount.subtract(back);
|
||||
payment.setAllocAmount(nextAlloc.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : nextAlloc.setScale(2, RoundingMode.HALF_UP));
|
||||
payPaymentMapper.updatePayPaymentStatus(payment);
|
||||
}
|
||||
return back;
|
||||
}
|
||||
|
||||
private void undoAlloc(PayPayment payment, PayBill bill, BigDecimal allocAmount, String operator)
|
||||
{
|
||||
BigDecimal paid = bill.getPaidAmount() == null ? BigDecimal.ZERO : bill.getPaidAmount();
|
||||
BigDecimal next = paid.subtract(allocAmount);
|
||||
if (next.compareTo(BigDecimal.ZERO) < 0)
|
||||
{
|
||||
next = BigDecimal.ZERO;
|
||||
}
|
||||
next = next.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal total = bill.getTotalAmount() == null ? BigDecimal.ZERO : bill.getTotalAmount();
|
||||
String from = bill.getStatus();
|
||||
bill.setPaidAmount(next);
|
||||
if (next.compareTo(BigDecimal.ZERO) == 0)
|
||||
{
|
||||
bill.setStatus("CONFIRMED");
|
||||
}
|
||||
else if (next.compareTo(total) >= 0)
|
||||
{
|
||||
bill.setStatus("PAID");
|
||||
}
|
||||
else
|
||||
{
|
||||
bill.setStatus("PARTIAL");
|
||||
}
|
||||
bill.setUpdateBy(operator);
|
||||
if (bill.getSupplierAmount() == null)
|
||||
{
|
||||
bill.setSupplierAmount(total);
|
||||
}
|
||||
if (bill.getDiffAmount() == null)
|
||||
{
|
||||
bill.setDiffAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
if (bill.getOrderCount() == null)
|
||||
{
|
||||
bill.setOrderCount(0);
|
||||
}
|
||||
payBillMapper.updatePayBillMoney(bill);
|
||||
PayBillLog log = new PayBillLog();
|
||||
log.setBillId(bill.getBillId());
|
||||
log.setBizType("PAYMENT");
|
||||
log.setBizId(payment.getPaymentId());
|
||||
log.setOperationType("REVERSE");
|
||||
log.setFromStatus(from);
|
||||
log.setToStatus(bill.getStatus());
|
||||
log.setOperationDesc("付款 " + payment.getPaymentNo() + " 反核销 " + allocAmount);
|
||||
log.setAmountBefore(paid);
|
||||
log.setAmountAfter(next);
|
||||
log.setOperatorName(operator);
|
||||
payBillMapper.insertLog(log);
|
||||
}
|
||||
|
||||
private void applyAlloc(PayPayment payment, PayBill bill, BigDecimal allocAmount, String operator)
|
||||
{
|
||||
BigDecimal paid = bill.getPaidAmount() == null ? BigDecimal.ZERO : bill.getPaidAmount();
|
||||
BigDecimal next = paid.add(allocAmount).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal total = bill.getTotalAmount() == null ? BigDecimal.ZERO : bill.getTotalAmount();
|
||||
String from = bill.getStatus();
|
||||
bill.setPaidAmount(next);
|
||||
bill.setStatus(next.compareTo(total) >= 0 ? "PAID" : "PARTIAL");
|
||||
bill.setUpdateBy(operator);
|
||||
if (bill.getSupplierAmount() == null)
|
||||
{
|
||||
bill.setSupplierAmount(total);
|
||||
}
|
||||
if (bill.getDiffAmount() == null)
|
||||
{
|
||||
bill.setDiffAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
if (bill.getOrderCount() == null)
|
||||
{
|
||||
bill.setOrderCount(0);
|
||||
}
|
||||
payBillMapper.updatePayBillMoney(bill);
|
||||
PayBillLog log = new PayBillLog();
|
||||
log.setBillId(bill.getBillId());
|
||||
log.setBizType("PAYMENT");
|
||||
log.setBizId(payment.getPaymentId());
|
||||
log.setOperationType("PAY");
|
||||
log.setFromStatus(from);
|
||||
log.setToStatus(bill.getStatus());
|
||||
log.setOperationDesc("付款 " + payment.getPaymentNo() + " 核销 " + allocAmount);
|
||||
log.setAmountBefore(paid);
|
||||
log.setAmountAfter(next);
|
||||
log.setOperatorName(operator);
|
||||
payBillMapper.insertLog(log);
|
||||
}
|
||||
|
||||
private BigDecimal openAmount(PayBill bill)
|
||||
{
|
||||
BigDecimal total = bill.getTotalAmount() == null ? BigDecimal.ZERO : bill.getTotalAmount();
|
||||
BigDecimal paid = bill.getPaidAmount() == null ? BigDecimal.ZERO : bill.getPaidAmount();
|
||||
return total.subtract(paid);
|
||||
}
|
||||
|
||||
private PayPayment required(Long paymentId)
|
||||
{
|
||||
PayPayment payment = paymentId == null ? null : payPaymentMapper.selectPayPaymentById(paymentId);
|
||||
if (payment == null)
|
||||
{
|
||||
throw PaymentMesEnum.BMS_PAY_PAY_MISS.exception();
|
||||
}
|
||||
return payment;
|
||||
}
|
||||
|
||||
private String nextNo()
|
||||
{
|
||||
return "FK" + new SimpleDateFormat("yyyyMM").format(new Date()) + IdUtils.fastSimpleUUID().substring(0, 8).toUpperCase();
|
||||
}
|
||||
}
|
||||
+563
@@ -0,0 +1,563 @@
|
||||
package com.bms.price.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
import com.bms.price.domain.PayProductTerm;
|
||||
import com.bms.price.domain.PaySurchargeRule;
|
||||
import com.bms.price.domain.PaySurchargeRuleSave;
|
||||
import com.bms.price.mapper.PayProductMapper;
|
||||
import com.bms.price.mapper.PayProductTermMapper;
|
||||
import com.bms.price.mapper.PaySurchargeRuleMapper;
|
||||
import com.bms.price.service.IPaySurchargeRuleService;
|
||||
|
||||
@Service
|
||||
public class PaySurchargeRuleServiceImpl implements IPaySurchargeRuleService
|
||||
{
|
||||
private static final Set<String> RULES = Set.of("ALWAYS", "POSTCODE", "ZONE", "EXTRA_KG", "WEIGHT", "SIZE", "RETURN");
|
||||
|
||||
private static final Set<String> NUMBERS = Set.of(
|
||||
"longestMin", "longestMax", "secondMin", "secondMax", "sumMin", "sumMax", "girthMin", "girthMax",
|
||||
"boxLength", "boxWidth", "boxHeight", "volumeMin", "volumeMax", "cubicMax", "weightMin", "weightMax",
|
||||
"exceedStepCm", "exceedRatio", "thresholdKg", "stepKg", "minCharge", "ratio");
|
||||
|
||||
@Autowired
|
||||
private PaySurchargeRuleMapper ruleMapper;
|
||||
|
||||
@Autowired
|
||||
private PayProductMapper productMapper;
|
||||
|
||||
@Autowired
|
||||
private PayProductTermMapper termMapper;
|
||||
|
||||
@Override
|
||||
public List<PaySurchargeRule> list(Long productId, Long termId)
|
||||
{
|
||||
if (productId == null || termId == null)
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
List<PaySurchargeRule> rows = ruleMapper.selectByProduct(productId, termId);
|
||||
rows.forEach(PaySurchargeRuleServiceImpl::hydrate);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PaySurchargeRule> previous(Long productId)
|
||||
{
|
||||
if (productId == null)
|
||||
{
|
||||
throw new ServiceException("请选择产品版本");
|
||||
}
|
||||
PaySurchargeRule previous = ruleMapper.selectPreviousVersion(productId);
|
||||
if (previous == null || previous.getProductId() == null || previous.getTermId() == null)
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
List<PaySurchargeRule> rows = ruleMapper.selectByProduct(previous.getProductId(), previous.getTermId());
|
||||
rows.forEach(PaySurchargeRuleServiceImpl::hydrate);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(PaySurchargeRuleSave command)
|
||||
{
|
||||
if (command == null || command.getProducts() == null || command.getProducts().isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择要绑定的产品");
|
||||
}
|
||||
List<PaySurchargeRule> rules = normalize(command.getRules());
|
||||
String operator = SecurityUtils.getUsername();
|
||||
Map<String, PaySurchargeRuleSave.PaySurchargeRuleBind> versions = new LinkedHashMap<>();
|
||||
for (PaySurchargeRuleSave.PaySurchargeRuleBind bind : command.getProducts())
|
||||
{
|
||||
if (bind == null || bind.getProductId() == null || bind.getTermId() == null)
|
||||
{
|
||||
throw new ServiceException("产品还没有可绑定的生效版本");
|
||||
}
|
||||
versions.put(bind.getProductId() + ":" + bind.getTermId(), bind);
|
||||
}
|
||||
for (PaySurchargeRuleSave.PaySurchargeRuleBind bind : versions.values())
|
||||
{
|
||||
PayProduct product = productMapper.selectPayProductById(bind.getProductId());
|
||||
if (product == null)
|
||||
{
|
||||
throw new ServiceException("应付产品不存在");
|
||||
}
|
||||
PayProductTerm term = termMapper.selectById(bind.getTermId());
|
||||
if (term == null || term.getProductId() == null || !term.getProductId().equals(bind.getProductId()))
|
||||
{
|
||||
throw new ServiceException("计费条件必须跟这一版有效期走");
|
||||
}
|
||||
ruleMapper.deleteByTerm(bind.getProductId(), bind.getTermId());
|
||||
if (rules.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PaySurchargeRule> rows = new ArrayList<>();
|
||||
int sort = 0;
|
||||
String lastRule = "";
|
||||
for (PaySurchargeRule rule : rules)
|
||||
{
|
||||
if (!rule.getCalcRule().equals(lastRule))
|
||||
{
|
||||
sort = 0;
|
||||
lastRule = rule.getCalcRule();
|
||||
}
|
||||
PaySurchargeRule row = copy(rule);
|
||||
row.setProductId(bind.getProductId());
|
||||
row.setTermId(bind.getTermId());
|
||||
row.setVersionNo(term.getVersionNo() == null ? bind.getVersionNo() : term.getVersionNo());
|
||||
row.setSortNo(sort++);
|
||||
row.setCreateBy(operator);
|
||||
row.setUpdateBy(operator);
|
||||
rows.add(row);
|
||||
}
|
||||
ruleMapper.insertBatch(rows);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<PaySurchargeRule> normalize(List<PaySurchargeRule> raw)
|
||||
{
|
||||
List<PaySurchargeRule> rules = new ArrayList<>();
|
||||
if (raw == null)
|
||||
{
|
||||
return rules;
|
||||
}
|
||||
for (PaySurchargeRule rule : raw)
|
||||
{
|
||||
if (rule == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String calcRule = rule.getCalcRule() == null ? "" : rule.getCalcRule().trim().toUpperCase(Locale.ROOT);
|
||||
if (!RULES.contains(calcRule))
|
||||
{
|
||||
throw new ServiceException("计费条件不在可选范围内");
|
||||
}
|
||||
PaySurchargeRule row = new PaySurchargeRule();
|
||||
row.setCalcRule(calcRule);
|
||||
row.setEnabled("0".equals(rule.getEnabled()) ? "0" : "1");
|
||||
Map<String, Object> detail = cleanDetail(calcRule, rule.getDetail());
|
||||
row.setDetail(detail);
|
||||
row.setRuleDetail(detail.isEmpty() ? null : JSON.toJSONString(detail));
|
||||
row.setCalcParam(trim(summary(calcRule, detail), 2000));
|
||||
row.setPostcodeParam(trim(text(detail.get("postcodes")), 2000));
|
||||
row.setZoneName(trim(text(detail.get("zoneName")), 200));
|
||||
row.setWeightMin(weightMin(calcRule, detail));
|
||||
row.setWeightMax(decimal(detail.get("weightMax"), "重量上限"));
|
||||
rules.add(row);
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
private static void hydrate(PaySurchargeRule row)
|
||||
{
|
||||
Map<String, Object> detail = parse(row.getRuleDetail());
|
||||
if (detail.isEmpty())
|
||||
{
|
||||
detail = legacy(row);
|
||||
}
|
||||
row.setDetail(detail.isEmpty() ? null : detail);
|
||||
}
|
||||
|
||||
private static Map<String, Object> parse(String json)
|
||||
{
|
||||
if (json == null || json.isBlank())
|
||||
{
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
Map<String, Object> parsed = JSON.parseObject(json, new TypeReference<LinkedHashMap<String, Object>>() { });
|
||||
return parsed == null ? new LinkedHashMap<>() : parsed;
|
||||
}
|
||||
|
||||
/** 还没写明细的旧行,用原来的摘要列带回弹框。 */
|
||||
private static Map<String, Object> legacy(PaySurchargeRule row)
|
||||
{
|
||||
Map<String, Object> detail = new LinkedHashMap<>();
|
||||
String calc = row.getCalcParam();
|
||||
String postcode = row.getPostcodeParam();
|
||||
if ("POSTCODE".equals(row.getCalcRule()))
|
||||
{
|
||||
put(detail, "postcodeMode", "PREFIX");
|
||||
put(detail, "postcodes", postcode == null || postcode.isBlank() ? calc : postcode);
|
||||
}
|
||||
else if ("ZONE".equals(row.getCalcRule()))
|
||||
{
|
||||
put(detail, "zoneName", row.getZoneName());
|
||||
}
|
||||
else if ("EXTRA_KG".equals(row.getCalcRule()))
|
||||
{
|
||||
put(detail, "thresholdKg", row.getWeightMin() == null ? calc : row.getWeightMin());
|
||||
put(detail, "postcodes", postcode);
|
||||
put(detail, "weightBasis", "ACTUAL");
|
||||
}
|
||||
else if ("WEIGHT".equals(row.getCalcRule()))
|
||||
{
|
||||
put(detail, "weightMin", row.getWeightMin() == null ? calc : row.getWeightMin());
|
||||
put(detail, "weightBasis", "ACTUAL");
|
||||
}
|
||||
else if ("SIZE".equals(row.getCalcRule()))
|
||||
{
|
||||
put(detail, "matchMode", "ANY");
|
||||
put(detail, "remark", calc);
|
||||
}
|
||||
else
|
||||
{
|
||||
put(detail, "remark", calc);
|
||||
}
|
||||
if (row.getWeightMin() != null)
|
||||
{
|
||||
detail.putIfAbsent("weightMin", row.getWeightMin());
|
||||
}
|
||||
if (row.getWeightMax() != null)
|
||||
{
|
||||
detail.put("weightMax", row.getWeightMax());
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
private static Map<String, Object> cleanDetail(String calcRule, Map<String, Object> raw)
|
||||
{
|
||||
Map<String, Object> detail = new LinkedHashMap<>();
|
||||
if (raw != null)
|
||||
{
|
||||
raw.forEach((key, value) -> put(detail, key, value));
|
||||
}
|
||||
checkChoice(detail, "matchMode", Set.of("ANY", "ALL"), "命中方式");
|
||||
checkChoice(detail, "postcodeMode", Set.of("PREFIX", "EXACT"), "邮编匹配");
|
||||
checkChoice(detail, "weightBasis", Set.of("ACTUAL", "CHARGE"), "重量口径");
|
||||
checkChoice(detail, "rateMode", Set.of("FIXED", "RATIO"), "计费方式");
|
||||
checkChoice(detail, "chargeBase", Set.of("HANDLE", "DELIVERY", "FREIGHT_FEE"), "计费基数");
|
||||
checkChoice(detail, "chargeMode", Set.of("SAME", "AIR", "ROAD", "FIXED"), "退回方式");
|
||||
checkChoice(detail, "boxMode", Set.of("OVER", "UNDER"), "箱规比较");
|
||||
for (String key : NUMBERS)
|
||||
{
|
||||
if (!detail.containsKey(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BigDecimal number = decimal(detail.get(key), numberLabel(key));
|
||||
if (number == null)
|
||||
{
|
||||
detail.remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail.put(key, number);
|
||||
}
|
||||
}
|
||||
if ("SIZE".equals(calcRule))
|
||||
{
|
||||
require(detail.get("matchMode") != null, "超尺寸要选择任一命中或全部命中");
|
||||
require(hasSize(detail), "超尺寸至少要填最长边、次长边、三边之和、围长、箱规、体积或超出步长");
|
||||
require(boxComplete(detail), "箱规的长、宽、高要一起填");
|
||||
}
|
||||
else if ("WEIGHT".equals(calcRule))
|
||||
{
|
||||
require(detail.get("weightMin") != null || detail.get("weightMax") != null, "超重要填写重量下限或上限");
|
||||
}
|
||||
else if ("EXTRA_KG".equals(calcRule))
|
||||
{
|
||||
require(detail.get("thresholdKg") != null, "超重续收要填写超过多少公斤后开始续收");
|
||||
}
|
||||
else if ("POSTCODE".equals(calcRule) || "ZONE".equals(calcRule))
|
||||
{
|
||||
detail.clear();
|
||||
}
|
||||
else if ("RETURN".equals(calcRule))
|
||||
{
|
||||
require(detail.get("chargeMode") != null, "退件要选择退回方式");
|
||||
require(boxComplete(detail), "箱规的长、宽、高要一起填");
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
private static boolean hasSize(Map<String, Object> detail)
|
||||
{
|
||||
return present(detail, "longestMin", "longestMax", "secondMin", "secondMax", "sumMin", "sumMax",
|
||||
"girthMin", "girthMax", "boxLength", "volumeMin", "volumeMax", "cubicMax", "exceedStepCm");
|
||||
}
|
||||
|
||||
private static boolean boxComplete(Map<String, Object> detail)
|
||||
{
|
||||
boolean any = present(detail, "boxLength", "boxWidth", "boxHeight");
|
||||
boolean all = detail.get("boxLength") != null && detail.get("boxWidth") != null && detail.get("boxHeight") != null;
|
||||
return !any || all;
|
||||
}
|
||||
|
||||
private static boolean present(Map<String, Object> detail, String... keys)
|
||||
{
|
||||
for (String key : keys)
|
||||
{
|
||||
if (detail.get(key) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static BigDecimal weightMin(String calcRule, Map<String, Object> detail)
|
||||
{
|
||||
if ("EXTRA_KG".equals(calcRule) && detail.get("thresholdKg") != null)
|
||||
{
|
||||
return (BigDecimal) detail.get("thresholdKg");
|
||||
}
|
||||
return (BigDecimal) detail.get("weightMin");
|
||||
}
|
||||
|
||||
private static String summary(String calcRule, Map<String, Object> detail)
|
||||
{
|
||||
if ("POSTCODE".equals(calcRule))
|
||||
{
|
||||
return "按应付邮编配置判断,邮编前缀和完整邮编都认";
|
||||
}
|
||||
if ("ZONE".equals(calcRule))
|
||||
{
|
||||
return "按应付邮编配置的分区判断";
|
||||
}
|
||||
List<String> parts = new ArrayList<>();
|
||||
add(parts, choice(detail.get("matchMode"), Map.of("ANY", "任一命中", "ALL", "全部命中")));
|
||||
add(parts, choice(detail.get("postcodeMode"), Map.of("PREFIX", "邮编开头", "EXACT", "完整邮编")));
|
||||
add(parts, span("最长边", detail.get("longestMin"), detail.get("longestMax"), "cm"));
|
||||
add(parts, span("次长边", detail.get("secondMin"), detail.get("secondMax"), "cm"));
|
||||
add(parts, span("三边之和", detail.get("sumMin"), detail.get("sumMax"), "cm"));
|
||||
add(parts, span("围长", detail.get("girthMin"), detail.get("girthMax"), "cm"));
|
||||
if (detail.get("boxLength") != null)
|
||||
{
|
||||
String mode = "UNDER".equals(detail.get("boxMode")) ? "小于" : "超过";
|
||||
parts.add(mode + "箱规" + plain(detail.get("boxLength")) + "×" + plain(detail.get("boxWidth")) + "×" + plain(detail.get("boxHeight")) + "cm");
|
||||
}
|
||||
add(parts, span("体积", detail.get("volumeMin"), detail.get("volumeMax"), "m³"));
|
||||
if (detail.get("cubicMax") != null)
|
||||
{
|
||||
parts.add("长×宽×高≤" + plain(detail.get("cubicMax")) + "cm³");
|
||||
}
|
||||
if (detail.get("exceedStepCm") != null)
|
||||
{
|
||||
parts.add("每超出" + plain(detail.get("exceedStepCm")) + "cm");
|
||||
}
|
||||
if (detail.get("exceedRatio") != null)
|
||||
{
|
||||
parts.add("运费加收" + plain(detail.get("exceedRatio")) + "%");
|
||||
}
|
||||
add(parts, span("重量", detail.get("weightMin"), detail.get("weightMax"), "kg"));
|
||||
add(parts, choice(detail.get("weightBasis"), Map.of("ACTUAL", "实重", "CHARGE", "计费重")));
|
||||
if (detail.get("thresholdKg") != null)
|
||||
{
|
||||
parts.add("超过" + plain(detail.get("thresholdKg")) + "kg后续收");
|
||||
}
|
||||
if (detail.get("stepKg") != null)
|
||||
{
|
||||
parts.add("每" + plain(detail.get("stepKg")) + "kg");
|
||||
}
|
||||
if (detail.get("minCharge") != null)
|
||||
{
|
||||
parts.add("最低" + plain(detail.get("minCharge")));
|
||||
}
|
||||
add(parts, text(detail.get("zoneName")));
|
||||
add(parts, text(detail.get("postcodes")));
|
||||
add(parts, text(detail.get("countries")));
|
||||
add(parts, choice(detail.get("chargeMode"), Map.of("SAME", "同正向运费", "AIR", "空运退回", "ROAD", "陆运退回", "FIXED", "固定金额")));
|
||||
add(parts, choice(detail.get("rateMode"), Map.of("FIXED", "固定金额", "RATIO", "按比例")));
|
||||
if (detail.get("ratio") != null)
|
||||
{
|
||||
parts.add(plain(detail.get("ratio")) + "%");
|
||||
}
|
||||
String period = joinPeriod(text(detail.get("periodStart")), text(detail.get("periodEnd")));
|
||||
add(parts, period);
|
||||
if ("ALWAYS".equals(calcRule) || "RETURN".equals(calcRule) || "SIZE".equals(calcRule))
|
||||
{
|
||||
add(parts, text(detail.get("remark")));
|
||||
}
|
||||
return parts.isEmpty() ? null : String.join(";", parts);
|
||||
}
|
||||
|
||||
private static String span(String name, Object min, Object max, String unit)
|
||||
{
|
||||
if (min == null && max == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (min != null && max != null)
|
||||
{
|
||||
return name + " " + plain(min) + "<x≤" + plain(max) + unit;
|
||||
}
|
||||
if (min != null)
|
||||
{
|
||||
return name + ">" + plain(min) + unit;
|
||||
}
|
||||
return name + "≤" + plain(max) + unit;
|
||||
}
|
||||
|
||||
private static String joinPeriod(String start, String end)
|
||||
{
|
||||
if (start == null && end == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return (start == null ? "不限" : start) + "至" + (end == null ? "不限" : end);
|
||||
}
|
||||
|
||||
private static String choice(Object value, Map<String, String> labels)
|
||||
{
|
||||
String text = text(value);
|
||||
return text == null ? null : labels.get(text);
|
||||
}
|
||||
|
||||
private static void checkChoice(Map<String, Object> detail, String key, Set<String> allowed, String label)
|
||||
{
|
||||
String value = text(detail.get(key));
|
||||
if (value == null)
|
||||
{
|
||||
detail.remove(key);
|
||||
return;
|
||||
}
|
||||
if (!allowed.contains(value))
|
||||
{
|
||||
throw new ServiceException(label + "不在可选范围内");
|
||||
}
|
||||
detail.put(key, value);
|
||||
}
|
||||
|
||||
private static void put(Map<String, Object> detail, String key, Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (value instanceof String text)
|
||||
{
|
||||
if (text.isBlank())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (text.length() > 2000)
|
||||
{
|
||||
throw new ServiceException("条件内容不能超过2000字");
|
||||
}
|
||||
detail.put(key, text.trim());
|
||||
return;
|
||||
}
|
||||
detail.put(key, value);
|
||||
}
|
||||
|
||||
private static BigDecimal decimal(Object value, String label)
|
||||
{
|
||||
if (value == null || (value instanceof String text && text.isBlank()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return new BigDecimal(String.valueOf(value).trim());
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
throw new ServiceException(label + "要填写数字");
|
||||
}
|
||||
}
|
||||
|
||||
private static String numberLabel(String key)
|
||||
{
|
||||
return switch (key)
|
||||
{
|
||||
case "longestMin", "longestMax" -> "最长边";
|
||||
case "secondMin", "secondMax" -> "次长边";
|
||||
case "sumMin", "sumMax" -> "三边之和";
|
||||
case "girthMin", "girthMax" -> "围长";
|
||||
case "boxLength" -> "箱规长";
|
||||
case "boxWidth" -> "箱规宽";
|
||||
case "boxHeight" -> "箱规高";
|
||||
case "volumeMin", "volumeMax" -> "体积";
|
||||
case "cubicMax" -> "长宽高乘积";
|
||||
case "exceedStepCm" -> "超出步长";
|
||||
case "exceedRatio", "ratio" -> "比例";
|
||||
case "thresholdKg", "weightMin", "weightMax", "stepKg" -> "重量";
|
||||
case "minCharge" -> "最低消费";
|
||||
default -> "数值";
|
||||
};
|
||||
}
|
||||
|
||||
private static String text(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
|
||||
private static String plain(Object value)
|
||||
{
|
||||
if (value instanceof BigDecimal number)
|
||||
{
|
||||
return number.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
return text(value);
|
||||
}
|
||||
|
||||
private static void add(List<String> parts, String value)
|
||||
{
|
||||
if (value != null && !value.isBlank())
|
||||
{
|
||||
parts.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void require(boolean ok, String message)
|
||||
{
|
||||
if (!ok)
|
||||
{
|
||||
throw new ServiceException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static PaySurchargeRule copy(PaySurchargeRule rule)
|
||||
{
|
||||
PaySurchargeRule row = new PaySurchargeRule();
|
||||
row.setCalcRule(rule.getCalcRule());
|
||||
row.setEnabled(rule.getEnabled());
|
||||
row.setCalcParam(rule.getCalcParam());
|
||||
row.setRuleDetail(rule.getRuleDetail());
|
||||
row.setDetail(rule.getDetail());
|
||||
row.setPostcodeParam(rule.getPostcodeParam());
|
||||
row.setZoneName(rule.getZoneName());
|
||||
row.setWeightMin(rule.getWeightMin());
|
||||
row.setWeightMax(rule.getWeightMax());
|
||||
return row;
|
||||
}
|
||||
|
||||
private static String trim(String value, int max)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String text = value.trim();
|
||||
if (text.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (text.length() > max)
|
||||
{
|
||||
throw new ServiceException("条件内容不能超过" + max + "字");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,13 @@
|
||||
<select id="selectPayBillList" resultType="com.bms.price.domain.PayBill">
|
||||
select bill_id as billId, bill_no as billNo, supplier_id as supplierId, supplier_name as supplierName,
|
||||
currency, source_type as sourceType, period_start as periodStart, period_end as periodEnd,
|
||||
order_count as orderCount, total_amount as totalAmount, status, void_reason as voidReason,
|
||||
void_by as voidBy, void_time as voidTime, create_by as createBy, create_time as createTime, remark
|
||||
order_count as orderCount, total_amount as totalAmount, supplier_amount as supplierAmount,
|
||||
diff_amount as diffAmount, paid_amount as paidAmount, due_date as dueDate, status,
|
||||
bill_kind as billKind, original_bill_id as originalBillId, void_reason as voidReason,
|
||||
void_by as voidBy, void_time as voidTime, confirm_by as confirmBy, confirm_time as confirmTime,
|
||||
tax_rate as taxRate, amount_net as amountNet, tax_amount as taxAmount, exchange_rate as exchangeRate,
|
||||
base_amount as baseAmount, rate_date as rateDate, term_days as termDays, submit_by as submitBy,
|
||||
create_by as createBy, create_time as createTime, remark
|
||||
from pay_bill
|
||||
<where>
|
||||
<if test="billNo != null and billNo != ''">and bill_no like concat('%', #{billNo}, '%')</if>
|
||||
@@ -20,22 +25,30 @@
|
||||
<select id="selectPayBillById" resultType="com.bms.price.domain.PayBill">
|
||||
select bill_id as billId, bill_no as billNo, supplier_id as supplierId, supplier_name as supplierName,
|
||||
currency, source_type as sourceType, period_start as periodStart, period_end as periodEnd,
|
||||
order_count as orderCount, total_amount as totalAmount, status, void_reason as voidReason,
|
||||
void_by as voidBy, void_time as voidTime, create_by as createBy, create_time as createTime, remark
|
||||
order_count as orderCount, total_amount as totalAmount, supplier_amount as supplierAmount,
|
||||
diff_amount as diffAmount, paid_amount as paidAmount, due_date as dueDate, status,
|
||||
bill_kind as billKind, original_bill_id as originalBillId, void_reason as voidReason,
|
||||
void_by as voidBy, void_time as voidTime, confirm_by as confirmBy, confirm_time as confirmTime,
|
||||
tax_rate as taxRate, amount_net as amountNet, tax_amount as taxAmount, exchange_rate as exchangeRate,
|
||||
base_amount as baseAmount, rate_date as rateDate, term_days as termDays, submit_by as submitBy,
|
||||
create_by as createBy, create_time as createTime, remark
|
||||
from pay_bill where bill_id = #{billId}
|
||||
</select>
|
||||
|
||||
<select id="selectLines" resultType="com.bms.price.domain.PayBillLine">
|
||||
select line_id as lineId, bill_id as billId, order_bill_id as orderBillId, order_no as orderNo,
|
||||
lading_no as ladingNo, lading_fee_id as ladingFeeId, fee_name as feeName, amount, currency,
|
||||
price_date as priceDate, version_no as versionNo, remote_flag as remoteFlag,
|
||||
density_level as densityLevel, line_source as lineSource
|
||||
select line_id as lineId, bill_id as billId, fee_id as feeId, order_bill_id as orderBillId, order_no as orderNo,
|
||||
lading_no as ladingNo, lading_fee_id as ladingFeeId, fee_name as feeName, fee_source as feeSource,
|
||||
fee_level as feeLevel, amount, supplier_amount as supplierAmount, diff_amount as diffAmount,
|
||||
diff_status as diffStatus, currency, price_date as priceDate, version_no as versionNo,
|
||||
remote_flag as remoteFlag, density_level as densityLevel, line_source as lineSource
|
||||
from pay_bill_line where bill_id = #{billId} order by line_id
|
||||
</select>
|
||||
|
||||
<select id="selectLogs" resultType="com.bms.price.domain.PayBillLog">
|
||||
select log_id as logId, bill_id as billId, operation_type as operationType, from_status as fromStatus,
|
||||
to_status as toStatus, operation_desc as operationDesc, operator_name as operatorName, create_time as createTime
|
||||
select log_id as logId, bill_id as billId, biz_type as bizType, biz_id as bizId, line_id as lineId,
|
||||
fee_id as feeId, operation_type as operationType, from_status as fromStatus, to_status as toStatus,
|
||||
operation_desc as operationDesc, amount_before as amountBefore, amount_after as amountAfter,
|
||||
operator_name as operatorName, create_time as createTime
|
||||
from pay_bill_log where bill_id = #{billId} order by log_id
|
||||
</select>
|
||||
|
||||
@@ -48,9 +61,21 @@
|
||||
(select group_concat(concat(ifnull(nullif(trim(f.fee_name), ''), ifnull(f.fee_type, '费用')), ' ', format(f.amount, 2))
|
||||
order by field(f.fee_type, 'BASIC_FREIGHT', 'REGISTRATION_FEE', 'SURCHARGE', 'REFUND'), f.fee_id separator ';')
|
||||
from prc_pay_bill_fee f where f.bill_id = o.bill_id and f.is_deleted = '0') as feeText,
|
||||
(select count(*) from prc_pay_bill_fee f
|
||||
where f.bill_id = o.bill_id and ifnull(f.is_deleted, '0') = '0') as feeCount,
|
||||
(select count(*) from prc_pay_bill_fee f
|
||||
where f.bill_id = o.bill_id and ifnull(f.is_deleted, '0') = '0'
|
||||
and f.statement_line_id is null
|
||||
and not exists (
|
||||
select 1 from pay_bill_line l
|
||||
inner join pay_bill b on b.bill_id = l.bill_id
|
||||
where l.order_bill_id = o.bill_id
|
||||
and (l.fee_id is null or l.fee_id = '')
|
||||
and b.status not in ('VOID', 'RED')
|
||||
)) as unbilledCount,
|
||||
(select b.bill_no from pay_bill_line l
|
||||
inner join pay_bill b on b.bill_id = l.bill_id
|
||||
where l.order_bill_id = o.bill_id and b.status <> 'VOID'
|
||||
where l.order_bill_id = o.bill_id and b.status not in ('VOID', 'RED')
|
||||
limit 1) as occupiedBillNo
|
||||
from prc_order_bill o
|
||||
left join prc_order_bill_ext e on e.bill_id = o.bill_id
|
||||
@@ -81,8 +106,9 @@
|
||||
</select>
|
||||
|
||||
<select id="selectOrderFees" resultType="com.bms.price.domain.PrcPayBillFee">
|
||||
select fee_id as feeId, bill_id as billId, fee_name as feeName, amount, currency, fee_source as feeSource,
|
||||
source_id as sourceId, is_deleted as isDeleted
|
||||
select fee_id as feeId, bill_id as billId, pay_bill_id as payBillId, fee_type as feeType, fee_name as feeName,
|
||||
sc_id as scId, fee_level as feeLevel, amount, currency, fee_source as feeSource, source_id as sourceId,
|
||||
is_deleted as isDeleted
|
||||
from prc_pay_bill_fee
|
||||
where is_deleted = '0'
|
||||
and bill_id in
|
||||
@@ -93,10 +119,15 @@
|
||||
<insert id="insertPayBill" useGeneratedKeys="true" keyProperty="billId">
|
||||
insert into pay_bill (
|
||||
bill_no, supplier_id, supplier_name, currency, source_type, period_start, period_end,
|
||||
order_count, total_amount, status, create_by, create_time, remark
|
||||
order_count, total_amount, supplier_amount, diff_amount, paid_amount, due_date, status, bill_kind,
|
||||
original_bill_id, tax_rate, amount_net, tax_amount, exchange_rate, base_amount, rate_date, term_days,
|
||||
create_by, create_time, remark
|
||||
) values (
|
||||
#{billNo}, #{supplierId}, #{supplierName}, #{currency}, #{sourceType}, #{periodStart}, #{periodEnd},
|
||||
#{orderCount}, #{totalAmount}, 'DRAFT', #{createBy}, sysdate(), #{remark}
|
||||
#{orderCount}, #{totalAmount}, #{supplierAmount}, #{diffAmount}, #{paidAmount}, #{dueDate}, 'DRAFT',
|
||||
ifnull(#{billKind}, 'BLUE'), #{originalBillId}, ifnull(#{taxRate}, 0), ifnull(#{amountNet}, #{totalAmount}),
|
||||
ifnull(#{taxAmount}, 0), ifnull(#{exchangeRate}, 1), ifnull(#{baseAmount}, #{totalAmount}), #{rateDate},
|
||||
#{termDays}, #{createBy}, sysdate(), #{remark}
|
||||
)
|
||||
</insert>
|
||||
|
||||
@@ -113,18 +144,245 @@
|
||||
|
||||
<insert id="insertLines">
|
||||
insert into pay_bill_line (
|
||||
bill_id, order_bill_id, order_no, lading_no, lading_fee_id, fee_name, amount, currency,
|
||||
price_date, version_no, remote_flag, density_level, line_source
|
||||
bill_id, fee_id, order_bill_id, order_no, lading_no, lading_fee_id, fee_name, fee_source, fee_level,
|
||||
amount, supplier_amount, diff_amount, diff_status, currency, price_date, version_no, remote_flag,
|
||||
density_level, line_source
|
||||
) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.billId}, #{item.orderBillId}, #{item.orderNo}, #{item.ladingNo}, #{item.ladingFeeId},
|
||||
#{item.feeName}, #{item.amount}, #{item.currency}, #{item.priceDate}, #{item.versionNo},
|
||||
#{item.remoteFlag}, #{item.densityLevel}, #{item.lineSource})
|
||||
(#{item.billId}, #{item.feeId}, #{item.orderBillId}, #{item.orderNo}, #{item.ladingNo}, #{item.ladingFeeId},
|
||||
#{item.feeName}, #{item.feeSource}, #{item.feeLevel}, #{item.amount}, #{item.supplierAmount},
|
||||
#{item.diffAmount}, ifnull(#{item.diffStatus}, 'NONE'), #{item.currency}, #{item.priceDate},
|
||||
#{item.versionNo}, #{item.remoteFlag}, #{item.densityLevel}, #{item.lineSource})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<insert id="insertLine" useGeneratedKeys="true" keyProperty="lineId">
|
||||
insert into pay_bill_line (
|
||||
bill_id, fee_id, order_bill_id, order_no, lading_no, lading_fee_id, fee_name, fee_source, fee_level,
|
||||
amount, supplier_amount, diff_amount, diff_status, currency, price_date, version_no, remote_flag,
|
||||
density_level, line_source
|
||||
) values (
|
||||
#{billId}, #{feeId}, #{orderBillId}, #{orderNo}, #{ladingNo}, #{ladingFeeId}, #{feeName}, #{feeSource},
|
||||
#{feeLevel}, #{amount}, #{supplierAmount}, #{diffAmount}, ifnull(#{diffStatus}, 'NONE'), #{currency},
|
||||
#{priceDate}, #{versionNo}, #{remoteFlag}, #{densityLevel}, #{lineSource}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="bindFeeLine">
|
||||
update prc_pay_bill_fee
|
||||
set statement_line_id = #{lineId}
|
||||
where fee_id = #{feeId}
|
||||
and statement_line_id is null
|
||||
and ifnull(is_deleted, '0') = '0'
|
||||
</update>
|
||||
|
||||
<update id="clearFeeLine">
|
||||
update prc_pay_bill_fee set statement_line_id = null where statement_line_id = #{lineId}
|
||||
</update>
|
||||
|
||||
<update id="clearFeeLines">
|
||||
update prc_pay_bill_fee f
|
||||
inner join pay_bill_line l on l.line_id = f.statement_line_id
|
||||
set f.statement_line_id = null
|
||||
where l.bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="updatePayBillMoney">
|
||||
update pay_bill
|
||||
set total_amount = #{totalAmount},
|
||||
supplier_amount = #{supplierAmount},
|
||||
diff_amount = #{diffAmount},
|
||||
paid_amount = #{paidAmount},
|
||||
order_count = #{orderCount},
|
||||
status = #{status},
|
||||
confirm_by = #{confirmBy},
|
||||
confirm_time = #{confirmTime},
|
||||
tax_rate = ifnull(#{taxRate}, 0),
|
||||
amount_net = ifnull(#{amountNet}, #{totalAmount}),
|
||||
tax_amount = ifnull(#{taxAmount}, 0),
|
||||
exchange_rate = ifnull(#{exchangeRate}, 1),
|
||||
base_amount = ifnull(#{baseAmount}, #{totalAmount}),
|
||||
rate_date = #{rateDate},
|
||||
term_days = #{termDays},
|
||||
update_by = #{updateBy},
|
||||
update_time = sysdate()
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="updateLineAmount">
|
||||
update pay_bill_line
|
||||
set supplier_amount = #{supplierAmount},
|
||||
diff_amount = #{diffAmount},
|
||||
diff_status = #{diffStatus}
|
||||
where line_id = #{lineId}
|
||||
and bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteOpenDiff">
|
||||
delete from pay_bill_diff where line_id = #{lineId} and handle_type = 'OPEN'
|
||||
</delete>
|
||||
|
||||
<delete id="deleteLine">
|
||||
delete from pay_bill_line where bill_id = #{billId} and line_id = #{lineId}
|
||||
</delete>
|
||||
|
||||
<select id="selectFreeFees" resultType="com.bms.price.domain.PrcPayBillFee">
|
||||
select f.fee_id as feeId, f.bill_id as billId, f.pay_bill_id as payBillId, f.fee_type as feeType,
|
||||
f.fee_name as feeName, f.sc_id as scId, f.fee_level as feeLevel, f.amount, f.currency,
|
||||
f.fee_source as feeSource, f.source_id as sourceId, f.settle_supplier_id as settleSupplierId,
|
||||
f.settle_supplier_name as settleSupplierName
|
||||
from prc_pay_bill_fee f
|
||||
where ifnull(f.is_deleted, '0') = '0'
|
||||
and f.statement_line_id is null
|
||||
and f.bill_id in
|
||||
<foreach collection="billIds" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
and not exists (
|
||||
select 1 from pay_bill_line l
|
||||
inner join pay_bill b on b.bill_id = l.bill_id
|
||||
where l.order_bill_id = f.bill_id
|
||||
and (l.fee_id is null or l.fee_id = '')
|
||||
and b.status not in ('VOID', 'RED')
|
||||
)
|
||||
order by f.bill_id, f.fee_id
|
||||
</select>
|
||||
|
||||
<select id="countCalcFees" resultType="int">
|
||||
select count(*)
|
||||
from prc_pay_bill_fee
|
||||
where bill_id = #{billId}
|
||||
and ifnull(is_deleted, '0') = '0'
|
||||
and fee_source = 'CALC'
|
||||
</select>
|
||||
|
||||
<select id="selectDiffs" resultType="com.bms.price.domain.PayBillDiff">
|
||||
select diff_id as diffId, bill_id as billId, line_id as lineId, fee_id as feeId, our_amount as ourAmount,
|
||||
supplier_amount as supplierAmount, diff_amount as diffAmount, reason, handle_type as handleType,
|
||||
adjust_fee_id as adjustFeeId, create_by as createBy, create_time as createTime
|
||||
from pay_bill_diff where bill_id = #{billId} order by diff_id
|
||||
</select>
|
||||
|
||||
<select id="selectFiles" resultType="com.bms.price.domain.PayBillFile">
|
||||
select file_id as fileId, bill_id as billId, file_type as fileType, invoice_no as invoiceNo,
|
||||
invoice_date as invoiceDate, amount, tax_amount as taxAmount, amount_net as amountNet,
|
||||
seller_name as sellerName, matched_amount as matchedAmount, currency, file_name as fileName,
|
||||
file_url as fileUrl, deleted, create_by as createBy, create_time as createTime
|
||||
from pay_bill_file
|
||||
where bill_id = #{billId} and deleted = '0'
|
||||
order by file_id
|
||||
</select>
|
||||
|
||||
<select id="selectApprovals" resultType="com.bms.price.domain.PayBillApproval">
|
||||
select approval_id as approvalId, bill_id as billId, action, opinion, operator_name as operatorName,
|
||||
create_time as createTime
|
||||
from pay_bill_approval where bill_id = #{billId} order by approval_id
|
||||
</select>
|
||||
|
||||
<select id="selectLine" resultType="com.bms.price.domain.PayBillLine">
|
||||
select line_id as lineId, bill_id as billId, fee_id as feeId, order_bill_id as orderBillId, order_no as orderNo,
|
||||
lading_no as ladingNo, fee_name as feeName, fee_source as feeSource, fee_level as feeLevel, amount,
|
||||
supplier_amount as supplierAmount, diff_amount as diffAmount, diff_status as diffStatus, currency,
|
||||
line_source as lineSource
|
||||
from pay_bill_line where line_id = #{lineId}
|
||||
</select>
|
||||
|
||||
<select id="selectOpenDiff" resultType="com.bms.price.domain.PayBillDiff">
|
||||
select diff_id as diffId, bill_id as billId, line_id as lineId, fee_id as feeId, our_amount as ourAmount,
|
||||
supplier_amount as supplierAmount, diff_amount as diffAmount, reason, handle_type as handleType,
|
||||
adjust_fee_id as adjustFeeId
|
||||
from pay_bill_diff
|
||||
where line_id = #{lineId} and handle_type = 'OPEN'
|
||||
order by diff_id desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insertDiff" useGeneratedKeys="true" keyProperty="diffId">
|
||||
insert into pay_bill_diff (
|
||||
bill_id, line_id, fee_id, our_amount, supplier_amount, diff_amount, reason, handle_type, adjust_fee_id,
|
||||
create_by, create_time
|
||||
) values (
|
||||
#{billId}, #{lineId}, #{feeId}, #{ourAmount}, #{supplierAmount}, #{diffAmount}, #{reason}, #{handleType},
|
||||
#{adjustFeeId}, #{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateDiff">
|
||||
update pay_bill_diff
|
||||
set our_amount = #{ourAmount},
|
||||
supplier_amount = #{supplierAmount},
|
||||
diff_amount = #{diffAmount},
|
||||
reason = #{reason},
|
||||
handle_type = #{handleType},
|
||||
adjust_fee_id = #{adjustFeeId}
|
||||
where diff_id = #{diffId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteDiffsByLine">
|
||||
delete from pay_bill_diff where line_id = #{lineId}
|
||||
</delete>
|
||||
|
||||
<insert id="insertFile" useGeneratedKeys="true" keyProperty="fileId">
|
||||
insert into pay_bill_file (
|
||||
bill_id, file_type, invoice_no, invoice_date, amount, tax_amount, amount_net, seller_name, matched_amount,
|
||||
currency, file_name, file_url, deleted, create_by, create_time
|
||||
) values (
|
||||
#{billId}, #{fileType}, #{invoiceNo}, #{invoiceDate}, #{amount}, #{taxAmount}, #{amountNet}, #{sellerName},
|
||||
ifnull(#{matchedAmount}, 0), #{currency}, #{fileName}, #{fileUrl}, '0', #{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="selectFile" resultType="com.bms.price.domain.PayBillFile">
|
||||
select file_id as fileId, bill_id as billId, file_type as fileType, invoice_no as invoiceNo,
|
||||
file_name as fileName, file_url as fileUrl, deleted
|
||||
from pay_bill_file where file_id = #{fileId}
|
||||
</select>
|
||||
|
||||
<update id="markFileDeleted">
|
||||
update pay_bill_file set deleted = '1' where file_id = #{fileId}
|
||||
</update>
|
||||
|
||||
<insert id="insertApproval">
|
||||
insert into pay_bill_approval (bill_id, action, opinion, operator_name, create_time)
|
||||
values (#{billId}, #{action}, #{opinion}, #{operatorName}, sysdate())
|
||||
</insert>
|
||||
|
||||
<select id="selectFileList" resultType="com.bms.price.domain.PayBillFile">
|
||||
select f.file_id as fileId, f.bill_id as billId, b.bill_no as billNo, b.supplier_name as supplierName,
|
||||
f.file_type as fileType, f.invoice_no as invoiceNo, f.invoice_date as invoiceDate, f.amount,
|
||||
f.tax_amount as taxAmount, f.file_name as fileName, f.file_url as fileUrl, f.create_by as createBy,
|
||||
f.create_time as createTime
|
||||
from pay_bill_file f
|
||||
inner join pay_bill b on b.bill_id = f.bill_id
|
||||
where f.deleted = '0'
|
||||
<if test="billNo != null and billNo != ''">and b.bill_no like concat('%', #{billNo}, '%')</if>
|
||||
<if test="supplierName != null and supplierName != ''">and b.supplier_name like concat('%', #{supplierName}, '%')</if>
|
||||
<if test="invoiceNo != null and invoiceNo != ''">and f.invoice_no like concat('%', #{invoiceNo}, '%')</if>
|
||||
<if test="fileType != null and fileType != ''">and f.file_type = #{fileType}</if>
|
||||
order by f.file_id desc
|
||||
</select>
|
||||
|
||||
<select id="countActiveFiles" resultType="int">
|
||||
select count(*) from pay_bill_file where bill_id = #{billId} and deleted = '0'
|
||||
</select>
|
||||
|
||||
<select id="selectOpenBills" resultType="com.bms.price.domain.PayPaymentAlloc">
|
||||
select bill_id as billId, bill_no as billNo, currency, supplier_id as supplierId, supplier_name as supplierName,
|
||||
(total_amount - ifnull(paid_amount, 0)) as openAmount
|
||||
from pay_bill
|
||||
where supplier_id = #{supplierId}
|
||||
and currency = #{currency}
|
||||
and status in ('CONFIRMED', 'PARTIAL')
|
||||
and ifnull(bill_kind, 'BLUE') <> 'RED'
|
||||
and total_amount - ifnull(paid_amount, 0) > 0
|
||||
order by bill_id
|
||||
</select>
|
||||
|
||||
<insert id="insertLog">
|
||||
insert into pay_bill_log (bill_id, operation_type, from_status, to_status, operation_desc, operator_name, create_time)
|
||||
values (#{billId}, #{operationType}, #{fromStatus}, #{toStatus}, #{operationDesc}, #{operatorName}, sysdate())
|
||||
insert into pay_bill_log (
|
||||
bill_id, biz_type, biz_id, line_id, fee_id, operation_type, from_status, to_status, operation_desc,
|
||||
amount_before, amount_after, operator_name, create_time
|
||||
) values (
|
||||
#{billId}, #{bizType}, #{bizId}, #{lineId}, #{feeId}, #{operationType}, #{fromStatus}, #{toStatus},
|
||||
#{operationDesc}, #{amountBefore}, #{amountAfter}, #{operatorName}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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="com.bms.price.mapper.PayFinanceMapper">
|
||||
|
||||
<update id="updateFinanceHead">
|
||||
update pay_bill
|
||||
set tax_rate = #{taxRate},
|
||||
exchange_rate = #{exchangeRate},
|
||||
rate_date = #{rateDate},
|
||||
update_time = sysdate()
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="updateSubmitBy">
|
||||
update pay_bill set submit_by = #{submitBy} where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="updateLineTax">
|
||||
update pay_bill_line
|
||||
set tax_rate = #{taxRate},
|
||||
amount_net = round(amount / (1 + #{taxRate}), 4),
|
||||
tax_amount = round(amount - amount / (1 + #{taxRate}), 4),
|
||||
base_amount = round(amount * #{exchangeRate}, 4)
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<select id="countDuplicateInvoice" resultType="int">
|
||||
select count(*)
|
||||
from pay_bill_file f
|
||||
inner join pay_bill b on b.bill_id = f.bill_id
|
||||
where f.deleted = '0'
|
||||
and f.file_type = 'INVOICE'
|
||||
and f.invoice_no = #{invoiceNo}
|
||||
and b.supplier_id = #{supplierId}
|
||||
and b.status <> 'VOID'
|
||||
<if test="fileId != null">and f.file_id <> #{fileId}</if>
|
||||
</select>
|
||||
|
||||
<update id="markInvoiceMatched">
|
||||
update pay_bill_file
|
||||
set matched_amount = ifnull(amount, 0)
|
||||
where bill_id = #{billId}
|
||||
and deleted = '0'
|
||||
and file_type = 'INVOICE'
|
||||
</update>
|
||||
|
||||
<select id="selectLadingSupplier" resultType="long">
|
||||
select supplier_id
|
||||
from pay_lading
|
||||
where lading_no = #{ladingNo}
|
||||
and supplier_id is not null
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<update id="stampFee">
|
||||
update prc_pay_bill_fee
|
||||
set settle_supplier_id = #{supplierId},
|
||||
settle_supplier_name = #{supplierName}
|
||||
where fee_id = #{feeId}
|
||||
</update>
|
||||
|
||||
<update id="stampCalc">
|
||||
update prc_pay_bill_fee
|
||||
set settle_supplier_id = #{supplierId},
|
||||
settle_supplier_name = #{supplierName}
|
||||
where bill_id = #{billId}
|
||||
and ifnull(is_deleted, '0') = '0'
|
||||
and statement_line_id is null
|
||||
and (fee_source is null or fee_source = '' or fee_source = 'CALC')
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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="com.bms.price.mapper.PayPaymentMapper">
|
||||
|
||||
<select id="selectPayPaymentList" resultType="com.bms.price.domain.PayPayment">
|
||||
select payment_id as paymentId, payment_no as paymentNo, supplier_id as supplierId, supplier_name as supplierName,
|
||||
currency, pay_date as payDate, pay_method as payMethod, bank_account as bankAccount, amount, bank_fee as bankFee,
|
||||
exchange_rate as exchangeRate, base_amount as baseAmount, alloc_amount as allocAmount,
|
||||
status, remark, create_by as createBy, create_time as createTime, confirm_by as confirmBy, confirm_time as confirmTime
|
||||
from pay_payment
|
||||
<where>
|
||||
<if test="paymentNo != null and paymentNo != ''">and payment_no like concat('%', #{paymentNo}, '%')</if>
|
||||
<if test="supplierName != null and supplierName != ''">and supplier_name like concat('%', #{supplierName}, '%')</if>
|
||||
<if test="status != null and status != ''">and status = #{status}</if>
|
||||
<if test="currency != null and currency != ''">and currency = #{currency}</if>
|
||||
</where>
|
||||
order by payment_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectPayPaymentById" resultType="com.bms.price.domain.PayPayment">
|
||||
select payment_id as paymentId, payment_no as paymentNo, supplier_id as supplierId, supplier_name as supplierName,
|
||||
currency, pay_date as payDate, pay_method as payMethod, bank_account as bankAccount, amount, bank_fee as bankFee,
|
||||
exchange_rate as exchangeRate, base_amount as baseAmount, alloc_amount as allocAmount,
|
||||
status, remark, create_by as createBy, create_time as createTime, confirm_by as confirmBy, confirm_time as confirmTime
|
||||
from pay_payment where payment_id = #{paymentId}
|
||||
</select>
|
||||
|
||||
<select id="selectAllocs" resultType="com.bms.price.domain.PayPaymentAlloc">
|
||||
select a.alloc_id as allocId, a.payment_id as paymentId, a.bill_id as billId, b.bill_no as billNo,
|
||||
a.alloc_amount as allocAmount, a.alloc_status as allocStatus, b.currency
|
||||
from pay_payment_alloc a
|
||||
left join pay_bill b on b.bill_id = a.bill_id
|
||||
where a.payment_id = #{paymentId}
|
||||
order by a.alloc_id
|
||||
</select>
|
||||
|
||||
<insert id="insertPayPayment" useGeneratedKeys="true" keyProperty="paymentId">
|
||||
insert into pay_payment (
|
||||
payment_no, supplier_id, supplier_name, currency, pay_date, pay_method, bank_account, amount, bank_fee,
|
||||
exchange_rate, status, remark, create_by, create_time
|
||||
) values (
|
||||
#{paymentNo}, #{supplierId}, #{supplierName}, #{currency}, #{payDate}, #{payMethod}, #{bankAccount},
|
||||
#{amount}, #{bankFee}, ifnull(#{exchangeRate}, 1), 'DRAFT', #{remark}, #{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertAlloc">
|
||||
insert into pay_payment_alloc (payment_id, bill_id, alloc_amount, alloc_status)
|
||||
values (#{paymentId}, #{billId}, #{allocAmount}, 'ACTIVE')
|
||||
</insert>
|
||||
|
||||
<update id="updatePayPaymentStatus">
|
||||
update pay_payment
|
||||
set status = #{status},
|
||||
confirm_by = #{confirmBy},
|
||||
confirm_time = #{confirmTime},
|
||||
exchange_rate = ifnull(#{exchangeRate}, exchange_rate),
|
||||
base_amount = ifnull(#{baseAmount}, base_amount),
|
||||
alloc_amount = ifnull(#{allocAmount}, alloc_amount)
|
||||
where payment_id = #{paymentId}
|
||||
</update>
|
||||
|
||||
<update id="reverseAlloc">
|
||||
update pay_payment_alloc
|
||||
set alloc_status = 'REVERSED',
|
||||
reverse_by = #{reverseBy},
|
||||
reverse_time = sysdate()
|
||||
where alloc_id = #{allocId}
|
||||
and alloc_status = 'ACTIVE'
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -155,8 +155,8 @@
|
||||
</select>
|
||||
|
||||
<select id="selectLinesByGroups" resultType="com.bms.price.domain.PayPostcodeLine">
|
||||
select line_id as lineId, group_id as groupId, region_name as regionName, zone_name as zoneName,
|
||||
postcode, postcode_end as postcodeEnd
|
||||
select line_id as lineId, group_id as groupId, country_name as countryName, country_code as countryCode,
|
||||
region_name as regionName, zone_name as zoneName, postcode, postcode_end as postcodeEnd
|
||||
from pay_postcode_line
|
||||
where group_id in
|
||||
<foreach collection="groupIds" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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="com.bms.price.mapper.PaySurchargeRuleMapper">
|
||||
|
||||
<select id="selectByProduct" resultType="com.bms.price.domain.PaySurchargeRule">
|
||||
select rule_id as ruleId, product_id as productId, term_id as termId, version_no as versionNo,
|
||||
calc_rule as calcRule, enabled, calc_param as calcParam, rule_detail as ruleDetail, postcode_param as postcodeParam,
|
||||
zone_name as zoneName, weight_min as weightMin, weight_max as weightMax, sort_no as sortNo
|
||||
from pay_surcharge_rule
|
||||
where product_id = #{productId}
|
||||
and term_id = #{termId}
|
||||
order by calc_rule, sort_no, rule_id
|
||||
</select>
|
||||
|
||||
<delete id="deleteByTerm">
|
||||
delete from pay_surcharge_rule
|
||||
where product_id = #{productId}
|
||||
and term_id = #{termId}
|
||||
</delete>
|
||||
|
||||
<insert id="insertBatch">
|
||||
insert into pay_surcharge_rule (
|
||||
product_id, term_id, version_no, calc_rule, enabled, calc_param, rule_detail, postcode_param,
|
||||
zone_name, weight_min, weight_max, sort_no, create_by, create_time, update_by, update_time
|
||||
) values
|
||||
<foreach collection="list" item="row" separator=",">
|
||||
(#{row.productId}, #{row.termId}, ifnull(#{row.versionNo}, 1), #{row.calcRule}, ifnull(#{row.enabled}, '1'),
|
||||
#{row.calcParam}, #{row.ruleDetail}, #{row.postcodeParam}, #{row.zoneName}, #{row.weightMin}, #{row.weightMax}, ifnull(#{row.sortNo}, 0),
|
||||
#{row.createBy}, sysdate(), #{row.updateBy}, sysdate())
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="selectPreviousVersion" resultType="com.bms.price.domain.PaySurchargeRule">
|
||||
select v.product_id as productId, t.term_id as termId, ifnull(v.version_no, 1) as versionNo
|
||||
from pay_product cur
|
||||
join pay_product v on v.dict_value = cur.dict_value
|
||||
and v.dict_value is not null and v.dict_value <> ''
|
||||
and v.product_id <> cur.product_id
|
||||
and (v.channel_id is null or v.channel_id = '')
|
||||
and ifnull(v.version_no, 1) < ifnull(cur.version_no, 1)
|
||||
join pay_product_term t on t.product_id = v.product_id and t.price_set_id is null
|
||||
where cur.product_id = #{productId}
|
||||
order by ifnull(v.version_no, 1) desc, t.term_id desc
|
||||
limit 1
|
||||
</select>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user