应收账单功能实现
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 公司收款账户配置。 */
|
||||
@Data
|
||||
public class ArBankAccount implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long bankAccountId;
|
||||
private String accountName;
|
||||
private String accountNo;
|
||||
private String bankName;
|
||||
private String currency;
|
||||
private String isDefault;
|
||||
private String status;
|
||||
private String remark;
|
||||
private String createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** 正式应收账单。 */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ArBill extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long arBillId;
|
||||
private String billNo;
|
||||
private String billName;
|
||||
private Long customerId;
|
||||
private String customerCode;
|
||||
private String customerName;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billPeriodStart;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billPeriodEnd;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billDate;
|
||||
private String currency;
|
||||
private Integer orderCount;
|
||||
private BigDecimal totalAmount;
|
||||
private BigDecimal writtenOffAmount;
|
||||
private BigDecimal unwrittenAmount;
|
||||
private String status;
|
||||
private Integer billVersion;
|
||||
private String bankAccountName;
|
||||
private String bankAccountNo;
|
||||
private String bankName;
|
||||
private String remark;
|
||||
private String confirmedBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date confirmedTime;
|
||||
|
||||
private List<ArBillDetail> details;
|
||||
private List<ArBillExport> exports;
|
||||
private List<ArBillLog> logs;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 账单订单快照。 */
|
||||
@Data
|
||||
public class ArBillDetail implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long detailId;
|
||||
private Long arBillId;
|
||||
private String prcBillId;
|
||||
private Integer detailNo;
|
||||
private String orderNo;
|
||||
private String trackingNo;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date receiveTime;
|
||||
private String productCode;
|
||||
private String productName;
|
||||
private String goodsDescription;
|
||||
private String countryCode;
|
||||
private String countryName;
|
||||
private String destPostcode;
|
||||
private BigDecimal actualWeight;
|
||||
private BigDecimal volumeWeight;
|
||||
private BigDecimal chargeWeight;
|
||||
private BigDecimal pkgLength;
|
||||
private BigDecimal pkgWidth;
|
||||
private BigDecimal pkgHeight;
|
||||
private String currency;
|
||||
private BigDecimal totalAmount;
|
||||
private String remark;
|
||||
private String createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private List<ArBillDetailFee> fees;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 账单费用快照。 */
|
||||
@Data
|
||||
public class ArBillDetailFee implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long detailFeeId;
|
||||
private Long detailId;
|
||||
private String prcFeeId;
|
||||
private Integer feeNo;
|
||||
private String feeType;
|
||||
private String feeName;
|
||||
private Long scId;
|
||||
private BigDecimal quantity;
|
||||
private String unit;
|
||||
private BigDecimal unitPrice;
|
||||
private BigDecimal amount;
|
||||
private String currency;
|
||||
private String calculationFormula;
|
||||
private String roundMode;
|
||||
private String remark;
|
||||
private String createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 账单导出文件元数据。 */
|
||||
@Data
|
||||
public class ArBillExport implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long exportId;
|
||||
private Long arBillId;
|
||||
private String fileName;
|
||||
private String fileType;
|
||||
private String templateCode;
|
||||
private String templateVersion;
|
||||
private String storageKey;
|
||||
private Long fileSize;
|
||||
private String fileHash;
|
||||
private Integer rowCount;
|
||||
private String exportStatus;
|
||||
private String exportBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date exportTime;
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 应收模块追加式审计日志。 */
|
||||
@Data
|
||||
public class ArBillLog implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long logId;
|
||||
private Long arBillId;
|
||||
private String objectType;
|
||||
private String objectId;
|
||||
/** 对象业务标识,例如账单号、收款单号或核销单号。 */
|
||||
private String objectDisplay;
|
||||
private String operationType;
|
||||
private String operationDesc;
|
||||
private String fromStatus;
|
||||
private String toStatus;
|
||||
private String beforeData;
|
||||
private String afterData;
|
||||
private String operatorId;
|
||||
private String operatorName;
|
||||
private String operationSource;
|
||||
private String requestId;
|
||||
private String traceId;
|
||||
private String result;
|
||||
private String errorMessage;
|
||||
private String clientIp;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** 应收真实收款。 */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ArReceipt extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long receiptId;
|
||||
private String receiptNo;
|
||||
private Long customerId;
|
||||
/** 客户名称,仅用于列表展示。 */
|
||||
private String customerName;
|
||||
/** 客户编码,仅用于列表展示。 */
|
||||
private String customerCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date receiptDate;
|
||||
private String currency;
|
||||
private BigDecimal receiptAmount;
|
||||
private BigDecimal unallocatedAmount;
|
||||
private String paymentMethod;
|
||||
private String bankReference;
|
||||
private String status;
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ruoyi.ar.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 收款与账单的核销关系。 */
|
||||
@Data
|
||||
public class ArReceiptWriteoff implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long writeoffId;
|
||||
private Long receiptId;
|
||||
private Long arBillId;
|
||||
/** 关联账单号,仅用于核销记录展示。 */
|
||||
private String billNo;
|
||||
/** 关联收款单号,仅用于核销记录展示。 */
|
||||
private String receiptNo;
|
||||
private String writeoffNo;
|
||||
private BigDecimal writeoffAmount;
|
||||
private String currency;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date writeoffDate;
|
||||
private String status;
|
||||
private String remark;
|
||||
private String createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private String cancelBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date cancelTime;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ruoyi.ar.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 账单状态动作参数。 */
|
||||
@Data
|
||||
public class ArBillActionRequest
|
||||
{
|
||||
private String remark;
|
||||
private String requestId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.ar.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** 生成应收账单前的订单计费结果查询条件。 */
|
||||
@Data
|
||||
public class ArBillGenerateQuery
|
||||
{
|
||||
private Long customerId;
|
||||
|
||||
private String customerCode;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billPeriodEnd;
|
||||
|
||||
private String currency;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ruoyi.ar.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 从订单计费结果生成正式账单的参数。 */
|
||||
@Data
|
||||
public class ArBillGenerateRequest
|
||||
{
|
||||
@NotNull(message = "客户不能为空")
|
||||
private Long customerId;
|
||||
@NotBlank(message = "账单名称不能为空")
|
||||
private String billName;
|
||||
private String customerCode;
|
||||
private String customerName;
|
||||
@NotNull(message = "账单截止日不能为空")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billPeriodEnd;
|
||||
@NotEmpty(message = "请至少选择一条订单")
|
||||
private List<String> sourceBillIds;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date billDate;
|
||||
private String currency = "CNY";
|
||||
private String bankAccountName;
|
||||
private String bankAccountNo;
|
||||
private String bankName;
|
||||
private String remark;
|
||||
private String requestId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.ar.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.ruoyi.ar.domain.ArReceipt;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** 收款查询参数。 */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ArReceiptQuery extends ArReceipt
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Date receiptDateStart;
|
||||
private Date receiptDateEnd;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.ruoyi.ar.domain.vo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 批量收款核销参数。 */
|
||||
@Data
|
||||
public class ArReceiptWriteoffRequest
|
||||
{
|
||||
@NotNull(message = "收款ID不能为空")
|
||||
private Long receiptId;
|
||||
@NotEmpty(message = "至少选择一张账单")
|
||||
@Valid
|
||||
private List<Allocation> allocations;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date writeoffDate;
|
||||
private String currency;
|
||||
private String remark;
|
||||
private String requestId;
|
||||
|
||||
@Data
|
||||
public static class Allocation
|
||||
{
|
||||
@NotNull(message = "账单ID不能为空")
|
||||
private Long arBillId;
|
||||
@NotNull(message = "核销金额不能为空")
|
||||
private BigDecimal amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收账单状态。数据库保存值使用 code,避免业务代码散落状态字符串。 */
|
||||
public enum ArBillStatus
|
||||
{
|
||||
DRAFT("DRAFT", "草稿"),
|
||||
CONFIRMED("CONFIRMED", "已确认"),
|
||||
PARTIAL_WRITTEN_OFF("PARTIAL_WRITTEN_OFF", "部分核销"),
|
||||
WRITTEN_OFF("WRITTEN_OFF", "已核销"),
|
||||
VOID("VOID", "已作废");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArBillStatus(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public boolean canConfirm()
|
||||
{
|
||||
return this == DRAFT;
|
||||
}
|
||||
|
||||
public boolean canVoid()
|
||||
{
|
||||
return this == DRAFT || this == CONFIRMED;
|
||||
}
|
||||
|
||||
public boolean canWriteOff()
|
||||
{
|
||||
return this == CONFIRMED || this == PARTIAL_WRITTEN_OFF;
|
||||
}
|
||||
|
||||
public static ArBillStatus of(String code)
|
||||
{
|
||||
for (ArBillStatus status : values())
|
||||
{
|
||||
if (status.code.equalsIgnoreCase(code))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean is(String code, ArBillStatus status)
|
||||
{
|
||||
return status != null && status.code.equalsIgnoreCase(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收账单导出任务状态。 */
|
||||
public enum ArExportStatus
|
||||
{
|
||||
PROCESSING("PROCESSING"),
|
||||
SUCCESS("SUCCESS"),
|
||||
FAILED("FAILED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ArExportStatus(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收操作日志结果。 */
|
||||
public enum ArLogResult
|
||||
{
|
||||
SUCCESS("SUCCESS"),
|
||||
FAILED("FAILED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ArLogResult(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 计费订单的应收账单占用状态。 */
|
||||
public enum ArOrderBillStatus
|
||||
{
|
||||
UNBILLED("UNBILLED"),
|
||||
BILLED("BILLED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ArOrderBillStatus(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收收款状态。 */
|
||||
public enum ArReceiptStatus
|
||||
{
|
||||
REGISTERED("REGISTERED"),
|
||||
PARTIAL_WRITTEN_OFF("PARTIAL_WRITTEN_OFF"),
|
||||
WRITTEN_OFF("WRITTEN_OFF"),
|
||||
CANCELLED("CANCELLED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ArReceiptStatus(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 收款核销状态。 */
|
||||
public enum ArWriteoffStatus
|
||||
{
|
||||
EFFECTIVE("EFFECTIVE"),
|
||||
CANCELLED("CANCELLED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ArWriteoffStatus(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.ar.domain.ArBankAccount;
|
||||
|
||||
public interface ArBankAccountMapper
|
||||
{
|
||||
ArBankAccount selectDefaultByCurrency(@Param("currency") String currency);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBillDetailFee;
|
||||
|
||||
public interface ArBillDetailFeeMapper
|
||||
{
|
||||
List<ArBillDetailFee> selectByDetailId(Long detailId);
|
||||
|
||||
List<ArBillDetailFee> selectByBillId(Long arBillId);
|
||||
|
||||
int insert(ArBillDetailFee fee);
|
||||
|
||||
int insertBatch(List<ArBillDetailFee> fees);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBillDetail;
|
||||
|
||||
public interface ArBillDetailMapper
|
||||
{
|
||||
List<ArBillDetail> selectByBillId(Long arBillId);
|
||||
|
||||
/** 查询账单明细及费用明细,避免详情页逐层查询。 */
|
||||
List<ArBillDetail> selectByBillIdWithFees(Long arBillId);
|
||||
|
||||
int insert(ArBillDetail detail);
|
||||
|
||||
int insertBatch(List<ArBillDetail> details);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBillExport;
|
||||
|
||||
public interface ArBillExportMapper
|
||||
{
|
||||
List<ArBillExport> selectByBillId(Long arBillId);
|
||||
|
||||
ArBillExport selectById(Long exportId);
|
||||
|
||||
ArBillExport selectLatestSuccessByBillId(Long arBillId);
|
||||
|
||||
int insert(ArBillExport export);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
|
||||
public interface ArBillLogMapper
|
||||
{
|
||||
List<ArBillLog> selectList(ArBillLog query);
|
||||
|
||||
ArBillLog selectSuccessByRequestId(@Param("requestId") String requestId,
|
||||
@Param("objectType") String objectType);
|
||||
|
||||
int insert(ArBillLog log);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
|
||||
public interface ArBillMapper
|
||||
{
|
||||
List<ArBill> selectArBillList(ArBill query);
|
||||
|
||||
ArBill selectArBillById(Long arBillId);
|
||||
|
||||
ArBill selectArBillByBillNo(String billNo);
|
||||
|
||||
Integer selectMaxBillVersion(@Param("customerId") Long customerId,
|
||||
@Param("cutoffDate") Date cutoffDate, @Param("currency") String currency);
|
||||
|
||||
ArBill selectArBillByIdForUpdate(Long arBillId);
|
||||
|
||||
int insertArBill(ArBill bill);
|
||||
|
||||
int updateArBillStatus(@Param("arBillId") Long arBillId, @Param("status") String status,
|
||||
@Param("remark") String remark, @Param("operator") String operator,
|
||||
@Param("fromStatus") String fromStatus);
|
||||
|
||||
int updateArBillAmounts(@Param("arBillId") Long arBillId,
|
||||
@Param("writtenOffAmount") BigDecimal writtenOffAmount,
|
||||
@Param("unwrittenAmount") BigDecimal unwrittenAmount,
|
||||
@Param("status") String status, @Param("operator") String operator,
|
||||
@Param("fromStatus") String fromStatus);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.ar.domain.ArReceipt;
|
||||
import com.ruoyi.ar.domain.vo.ArReceiptQuery;
|
||||
|
||||
public interface ArReceiptMapper
|
||||
{
|
||||
List<ArReceipt> selectArReceiptList(ArReceiptQuery query);
|
||||
|
||||
ArReceipt selectArReceiptById(Long receiptId);
|
||||
|
||||
ArReceipt selectArReceiptByIdForUpdate(Long receiptId);
|
||||
|
||||
ArReceipt selectArReceiptByNo(String receiptNo);
|
||||
|
||||
ArReceipt selectArReceiptByReference(@Param("paymentMethod") String paymentMethod,
|
||||
@Param("bankReference") String bankReference);
|
||||
|
||||
int insertArReceipt(ArReceipt receipt);
|
||||
|
||||
int updateBalanceAndStatus(@Param("receiptId") Long receiptId,
|
||||
@Param("unallocatedAmount") BigDecimal unallocatedAmount,
|
||||
@Param("status") String status, @Param("operator") String operator);
|
||||
|
||||
int updateCancelled(@Param("receiptId") Long receiptId, @Param("operator") String operator,
|
||||
@Param("remark") String remark);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ruoyi.ar.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.ar.domain.ArReceiptWriteoff;
|
||||
|
||||
public interface ArReceiptWriteoffMapper
|
||||
{
|
||||
List<ArReceiptWriteoff> selectByBillId(Long arBillId);
|
||||
|
||||
List<ArReceiptWriteoff> selectByReceiptId(Long receiptId);
|
||||
|
||||
ArReceiptWriteoff selectById(Long writeoffId);
|
||||
|
||||
ArReceiptWriteoff selectByIdForUpdate(Long writeoffId);
|
||||
|
||||
int insert(ArReceiptWriteoff writeoff);
|
||||
|
||||
int updateCancelled(@Param("writeoffId") Long writeoffId, @Param("cancelBy") String cancelBy,
|
||||
@Param("remark") String remark);
|
||||
|
||||
BigDecimal sumEffectiveByBillId(Long arBillId);
|
||||
|
||||
BigDecimal sumEffectiveByReceiptId(Long receiptId);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ruoyi.ar.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
|
||||
public interface IArBillLogService
|
||||
{
|
||||
List<ArBillLog> selectLogList(ArBillLog query);
|
||||
|
||||
void record(ArBillLog log);
|
||||
|
||||
void recordFailure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
String fromStatus, String message, String operator, String requestId);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.ar.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
import com.ruoyi.ar.domain.ArBankAccount;
|
||||
import com.ruoyi.ar.domain.ArBillExport;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.domain.vo.ArBillGenerateRequest;
|
||||
import com.ruoyi.ar.domain.vo.ArBillGenerateQuery;
|
||||
import com.ruoyi.ar.domain.vo.ArBillActionRequest;
|
||||
import com.ruoyi.price.domain.PrcOrderBill;
|
||||
|
||||
public interface IArBillService
|
||||
{
|
||||
List<ArBill> selectArBillList(ArBill query);
|
||||
|
||||
ArBill selectArBillById(Long arBillId);
|
||||
|
||||
ArBankAccount getDefaultBankAccount(String currency);
|
||||
|
||||
List<PrcOrderBill> previewBill(ArBillGenerateQuery query);
|
||||
|
||||
ArBill generateBill(ArBillGenerateRequest request, String operator, String requestId);
|
||||
|
||||
ArBill confirmBill(Long arBillId, ArBillActionRequest request, String operator, String requestId);
|
||||
|
||||
ArBill voidBill(Long arBillId, ArBillActionRequest request, String operator, String requestId);
|
||||
|
||||
ArBillExport exportBill(Long arBillId, String operator, String requestId);
|
||||
|
||||
List<ArBillExport> selectExports(Long arBillId);
|
||||
|
||||
void downloadExport(Long exportId, jakarta.servlet.http.HttpServletResponse response) throws Exception;
|
||||
|
||||
List<ArBillLog> selectLogList(ArBillLog query);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.ar.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArReceipt;
|
||||
import com.ruoyi.ar.domain.ArReceiptWriteoff;
|
||||
import com.ruoyi.ar.domain.vo.ArReceiptQuery;
|
||||
import com.ruoyi.ar.domain.vo.ArReceiptWriteoffRequest;
|
||||
|
||||
public interface IArReceiptService
|
||||
{
|
||||
List<ArReceipt> selectReceiptList(ArReceiptQuery query);
|
||||
|
||||
List<ArReceiptWriteoff> selectWriteoffs(Long receiptId);
|
||||
|
||||
ArReceipt registerReceipt(ArReceipt receipt, String operator, String requestId);
|
||||
|
||||
void cancelReceipt(Long receiptId, String remark, String operator, String requestId);
|
||||
|
||||
List<ArReceiptWriteoff> writeoff(ArReceiptWriteoffRequest request, String operator, String requestId);
|
||||
|
||||
void cancelWriteoff(Long writeoffId, String remark, String operator, String requestId);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.ruoyi.ar.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
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.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.mapper.ArBillLogMapper;
|
||||
import com.ruoyi.ar.service.IArBillLogService;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
|
||||
@Service
|
||||
public class ArBillLogServiceImpl implements IArBillLogService
|
||||
{
|
||||
@Autowired
|
||||
private ArBillLogMapper logMapper;
|
||||
|
||||
@Override
|
||||
public List<ArBillLog> selectLogList(ArBillLog query)
|
||||
{
|
||||
return logMapper.selectList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void record(ArBillLog log)
|
||||
{
|
||||
logMapper.insert(log);
|
||||
}
|
||||
|
||||
/** 失败日志必须独立提交,不能随业务异常一起回滚。 */
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void recordFailure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
String fromStatus, String message, String operator, String requestId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(arBillId);
|
||||
log.setObjectType(objectType);
|
||||
log.setObjectId(objectId);
|
||||
log.setOperationType(operationType);
|
||||
log.setOperationDesc(message);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setOperatorId(operator);
|
||||
log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB");
|
||||
log.setRequestId(requestId == null ? IdUtils.fastSimpleUUID() : requestId);
|
||||
log.setResult(ArLogResult.FAILED.getCode());
|
||||
log.setErrorMessage(message);
|
||||
logMapper.insert(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,902 @@
|
||||
package com.ruoyi.ar.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
import com.ruoyi.ar.domain.ArBankAccount;
|
||||
import com.ruoyi.ar.domain.ArBillDetail;
|
||||
import com.ruoyi.ar.domain.ArBillDetailFee;
|
||||
import com.ruoyi.ar.domain.ArBillExport;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArBillStatus;
|
||||
import com.ruoyi.ar.enums.ArExportStatus;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.enums.ArOrderBillStatus;
|
||||
import com.ruoyi.ar.mapper.ArBillDetailFeeMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillDetailMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillExportMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillLogMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillMapper;
|
||||
import com.ruoyi.ar.mapper.ArBankAccountMapper;
|
||||
import com.ruoyi.ar.service.IArBillLogService;
|
||||
import com.ruoyi.ar.service.IArBillService;
|
||||
import com.ruoyi.ar.domain.vo.ArBillActionRequest;
|
||||
import com.ruoyi.ar.domain.vo.ArBillGenerateQuery;
|
||||
import com.ruoyi.ar.domain.vo.ArBillGenerateRequest;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.FileUtils;
|
||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import com.ruoyi.price.domain.PrcIncomeBillFee;
|
||||
import com.ruoyi.price.domain.PrcOrderBill;
|
||||
import com.ruoyi.price.mapper.PrcIncomeBillFeeMapper;
|
||||
import com.ruoyi.price.mapper.PrcOrderBillMapper;
|
||||
|
||||
@Service
|
||||
public class ArBillServiceImpl implements IArBillService
|
||||
{
|
||||
private static final String EXPORT_TEMPLATE_RESOURCE = "templates/ar/ar_bill_template.xlsx";
|
||||
private static final int TEMPLATE_DATA_START_ROW = 4;
|
||||
private static final int TEMPLATE_FOOTER_START_ROW = 5;
|
||||
private static final int TEMPLATE_COLUMN_COUNT = 20;
|
||||
|
||||
@Autowired private ArBillMapper billMapper;
|
||||
@Autowired private ArBankAccountMapper bankAccountMapper;
|
||||
@Autowired private ArBillDetailMapper detailMapper;
|
||||
@Autowired private ArBillDetailFeeMapper detailFeeMapper;
|
||||
@Autowired private ArBillExportMapper exportMapper;
|
||||
@Autowired private ArBillLogMapper logMapper;
|
||||
@Autowired private IArBillLogService logService;
|
||||
@Autowired private PrcOrderBillMapper sourceBillMapper;
|
||||
@Autowired private PrcIncomeBillFeeMapper sourceFeeMapper;
|
||||
|
||||
@Override
|
||||
public List<ArBill> selectArBillList(ArBill query)
|
||||
{
|
||||
List<ArBill> bills = billMapper.selectArBillList(query);
|
||||
for (ArBill bill : bills)
|
||||
{
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
}
|
||||
return bills;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArBill selectArBillById(Long arBillId)
|
||||
{
|
||||
ArBill bill = billMapper.selectArBillById(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw new ServiceException("应收账单不存在");
|
||||
}
|
||||
fillDetails(bill);
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
bill.setExports(exportMapper.selectByBillId(arBillId));
|
||||
return bill;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArBankAccount getDefaultBankAccount(String currency)
|
||||
{
|
||||
ArBankAccount account = bankAccountMapper.selectDefaultByCurrency(
|
||||
StringUtils.isEmpty(currency) ? "CNY" : currency);
|
||||
if (account != null)
|
||||
{
|
||||
account.setAccountNo(maskAccount(account.getAccountNo()));
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcOrderBill> previewBill(ArBillGenerateQuery query)
|
||||
{
|
||||
validatePreviewQuery(query);
|
||||
return sourceBillMapper.selectPrcOrderBillByCustomerPeriod(query.getCustomerId(),
|
||||
query.getCustomerCode(), query.getBillPeriodEnd(), query.getCurrency(), ArOrderBillStatus.UNBILLED.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArBill generateBill(ArBillGenerateRequest request, String operator, String requestId)
|
||||
{
|
||||
validatePeriod(request);
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "BILL");
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
return selectArBillById(Long.valueOf(previous.getObjectId()));
|
||||
}
|
||||
|
||||
List<String> requestedSourceBillIds = normalizeSourceBillIds(request.getSourceBillIds());
|
||||
if (requestedSourceBillIds.isEmpty())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null, "请至少选择一条有效订单", operator, requestId);
|
||||
}
|
||||
String requestedCurrency = StringUtils.isEmpty(request.getCurrency()) ? "CNY" : request.getCurrency();
|
||||
List<PrcOrderBill> sources = sourceBillMapper.selectPrcOrderBillByCustomerCutoffAndIds(
|
||||
request.getCustomerId(), request.getCustomerCode(), request.getBillPeriodEnd(), requestedCurrency,
|
||||
requestedSourceBillIds, ArOrderBillStatus.UNBILLED.getCode());
|
||||
if (sources == null || sources.isEmpty() || sources.size() != requestedSourceBillIds.size())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
"部分所选订单已出账、已变更或不符合当前筛选条件,请重新查询后生成", operator, requestId);
|
||||
}
|
||||
|
||||
String currency = requestedCurrency;
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (PrcOrderBill source : sources)
|
||||
{
|
||||
if (source.getCurrency() != null && !currency.equalsIgnoreCase(source.getCurrency()))
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null, "截止日期内存在多个币种,不能合并为一张账单", operator, requestId);
|
||||
}
|
||||
total = total.add(nvl(source.getIncomeTotal()));
|
||||
}
|
||||
|
||||
ArBankAccount bankAccount = bankAccountMapper.selectDefaultByCurrency(currency);
|
||||
if (bankAccount == null)
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
"未配置 " + currency + " 币种的公司默认收款账户,请先完成收款账户配置", operator, requestId);
|
||||
}
|
||||
|
||||
ArBill bill = new ArBill();
|
||||
bill.setBillNo("AR" + new SimpleDateFormat("yyyyMM").format(new Date())
|
||||
+ IdUtils.fastSimpleUUID().substring(0, 8).toUpperCase());
|
||||
bill.setBillName(request.getBillName().trim());
|
||||
bill.setCustomerId(request.getCustomerId());
|
||||
bill.setCustomerCode(StringUtils.isEmpty(request.getCustomerCode()) ? sources.get(0).getCustomerCode() : request.getCustomerCode());
|
||||
bill.setCustomerName(StringUtils.isEmpty(request.getCustomerName()) ? sources.get(0).getCustomerName() : request.getCustomerName());
|
||||
bill.setBillPeriodStart(earliestBizDate(sources));
|
||||
bill.setBillPeriodEnd(request.getBillPeriodEnd());
|
||||
bill.setBillDate(request.getBillDate() == null ? new Date() : request.getBillDate());
|
||||
bill.setCurrency(currency);
|
||||
bill.setOrderCount(sources.size());
|
||||
bill.setTotalAmount(total);
|
||||
bill.setWrittenOffAmount(BigDecimal.ZERO);
|
||||
bill.setUnwrittenAmount(total);
|
||||
bill.setStatus(ArBillStatus.DRAFT.getCode());
|
||||
bill.setBankAccountName(bankAccount.getAccountName());
|
||||
bill.setBankAccountNo(bankAccount.getAccountNo());
|
||||
bill.setBankName(bankAccount.getBankName());
|
||||
bill.setRemark(request.getRemark());
|
||||
bill.setCreateBy(operator);
|
||||
bill.setUpdateBy(operator);
|
||||
|
||||
List<String> sourceBillIds = new ArrayList<>();
|
||||
for (PrcOrderBill source : sources)
|
||||
{
|
||||
sourceBillIds.add(source.getBillId());
|
||||
}
|
||||
int claimedCount = sourceBillMapper.claimArBillOrders(sourceBillIds, operator,
|
||||
ArOrderBillStatus.UNBILLED.getCode(), ArOrderBillStatus.BILLED.getCode());
|
||||
if (claimedCount != sourceBillIds.size())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
"部分计费订单已被其他账单占用,请重新查询后生成", operator, requestId);
|
||||
}
|
||||
|
||||
// 订单占用成功后再锁定版本号,避免并发生成时先查到同一个版本。
|
||||
Integer maxVersion = billMapper.selectMaxBillVersion(request.getCustomerId(), request.getBillPeriodEnd(), currency);
|
||||
bill.setBillVersion((maxVersion == null ? 0 : maxVersion) + 1);
|
||||
|
||||
billMapper.insertArBill(bill);
|
||||
|
||||
List<ArBillDetail> details = new ArrayList<>();
|
||||
int detailNo = 1;
|
||||
for (PrcOrderBill source : sources)
|
||||
{
|
||||
details.add(toDetail(bill, source, detailNo++, operator));
|
||||
}
|
||||
detailMapper.insertBatch(details);
|
||||
|
||||
// Batch insert does not need to expose generated IDs to the caller. Re-read the
|
||||
// persisted details once, then attach source fees in one batch query.
|
||||
Map<String, ArBillDetail> detailsBySourceBill = new HashMap<>();
|
||||
for (ArBillDetail detail : detailMapper.selectByBillId(bill.getArBillId()))
|
||||
{
|
||||
detailsBySourceBill.put(detail.getPrcBillId(), detail);
|
||||
}
|
||||
List<ArBillDetailFee> detailFees = new ArrayList<>();
|
||||
List<PrcIncomeBillFee> sourceFees = sourceBillIds.isEmpty()
|
||||
? new ArrayList<PrcIncomeBillFee>() : sourceFeeMapper.selectByBillIds(sourceBillIds);
|
||||
Map<String, Integer> feeNumbers = new HashMap<>();
|
||||
for (PrcIncomeBillFee sourceFee : sourceFees)
|
||||
{
|
||||
ArBillDetail detail = detailsBySourceBill.get(sourceFee.getBillId());
|
||||
if (detail == null)
|
||||
{
|
||||
throw new ServiceException("来源费用找不到对应的账单明细:" + sourceFee.getBillId());
|
||||
}
|
||||
int feeNo = feeNumbers.merge(sourceFee.getBillId(), 1, Integer::sum);
|
||||
detailFees.add(toDetailFee(detail, sourceFee, feeNo, operator));
|
||||
}
|
||||
if (!detailFees.isEmpty())
|
||||
{
|
||||
detailFeeMapper.insertBatch(detailFees);
|
||||
}
|
||||
recordSuccess(bill, "CREATE", "生成正式账单", null, ArBillStatus.DRAFT.getCode(), requestId, operator);
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
return bill;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArBill confirmBill(Long arBillId, ArBillActionRequest request, String operator, String requestId)
|
||||
{
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
ArBillStatus currentStatus = ArBillStatus.of(bill.getStatus());
|
||||
if (currentStatus == null || !currentStatus.canConfirm())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
"只有草稿账单可以确认,当前状态为 " + bill.getStatus(), operator, requestId);
|
||||
}
|
||||
if (!validateDetails(arBillId, bill.getTotalAmount()))
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
"账单费用明细汇总与订单或账单总额不一致", operator, requestId);
|
||||
}
|
||||
if (billMapper.updateArBillStatus(arBillId, ArBillStatus.CONFIRMED.getCode(),
|
||||
StringUtils.isEmpty(request == null ? null : request.getRemark()) ? bill.getRemark() : request.getRemark(),
|
||||
operator, ArBillStatus.DRAFT.getCode()) != 1)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
"账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
recordSuccess(bill, "CONFIRM", "确认应收账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.CONFIRMED.getCode(), requestId, operator);
|
||||
return selectArBillById(arBillId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArBill voidBill(Long arBillId, ArBillActionRequest request, String operator, String requestId)
|
||||
{
|
||||
String remark = request == null ? null : request.getRemark();
|
||||
if (StringUtils.isEmpty(remark))
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", null, "作废必须填写原因", operator, requestId);
|
||||
}
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
ArBillStatus currentStatus = ArBillStatus.of(bill.getStatus());
|
||||
if (currentStatus == null || !currentStatus.canVoid())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
"当前状态不允许作废", operator, requestId);
|
||||
}
|
||||
List<String> sourceBillIds = new ArrayList<>();
|
||||
for (ArBillDetail detail : detailMapper.selectByBillId(arBillId))
|
||||
{
|
||||
if (!StringUtils.isEmpty(detail.getPrcBillId()))
|
||||
{
|
||||
sourceBillIds.add(detail.getPrcBillId());
|
||||
}
|
||||
}
|
||||
if (billMapper.updateArBillStatus(arBillId, ArBillStatus.VOID.getCode(), remark, operator, currentStatus.getCode()) != 1)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
"账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
if (!sourceBillIds.isEmpty())
|
||||
{
|
||||
int releasedCount = sourceBillMapper.releaseArBillOrders(sourceBillIds, operator,
|
||||
ArOrderBillStatus.BILLED.getCode(), ArOrderBillStatus.UNBILLED.getCode());
|
||||
if (releasedCount != sourceBillIds.size())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
"部分来源订单状态异常,账单未作废,请刷新后重试", operator, requestId);
|
||||
}
|
||||
}
|
||||
recordSuccess(bill, "VOID", "作废应收账单:" + remark, bill.getStatus(), ArBillStatus.VOID.getCode(), requestId, operator);
|
||||
return selectArBillById(arBillId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArBillExport exportBill(Long arBillId, String operator, String requestId)
|
||||
{
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "EXPORT");
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
ArBillExport previousExport = exportMapper.selectById(Long.valueOf(previous.getObjectId()));
|
||||
if (previousExport != null)
|
||||
{
|
||||
return previousExport;
|
||||
}
|
||||
}
|
||||
ArBill bill = billMapper.selectArBillById(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
if (ArBillStatus.is(bill.getStatus(), ArBillStatus.DRAFT) || ArBillStatus.is(bill.getStatus(), ArBillStatus.VOID))
|
||||
{
|
||||
throw failure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", bill.getStatus(),
|
||||
"草稿或作废账单不可导出", operator, requestId);
|
||||
}
|
||||
fillDetails(bill);
|
||||
String fileName = bill.getBillNo() + "_" + safeFilePart(bill.getCustomerName()) + "_"
|
||||
+ new SimpleDateFormat("yyyyMM").format(bill.getBillPeriodEnd()) + ".xlsx";
|
||||
ArBillExport export = new ArBillExport();
|
||||
export.setArBillId(arBillId);
|
||||
export.setFileName(fileName);
|
||||
export.setFileType("XLSX");
|
||||
export.setTemplateCode("AR_CUSTOMER_BILL");
|
||||
export.setTemplateVersion("1.0");
|
||||
export.setExportBy(operator);
|
||||
export.setRowCount(bill.getOrderCount());
|
||||
try
|
||||
{
|
||||
byte[] data = buildWorkbook(bill);
|
||||
String relative = "ar/" + DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + ".xlsx";
|
||||
String downloadDir = RuoYiConfig.getProfile() + File.separator + "download";
|
||||
File file = FileUploadUtils.getAbsoluteFile(downloadDir, relative);
|
||||
Files.write(file.toPath(), data);
|
||||
export.setStorageKey(FileUploadUtils.getPathFileName(downloadDir, relative));
|
||||
export.setFileSize((long) data.length);
|
||||
export.setFileHash(sha256(data));
|
||||
export.setExportStatus(ArExportStatus.SUCCESS.getCode());
|
||||
exportMapper.insert(export);
|
||||
recordSuccess(bill, "EXPORT", "导出账单 XLSX", bill.getStatus(), bill.getStatus(), requestId, operator, export.getExportId());
|
||||
return export;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
export.setStorageKey("");
|
||||
export.setExportStatus(ArExportStatus.FAILED.getCode());
|
||||
export.setRemark(e.getMessage());
|
||||
exportMapper.insert(export);
|
||||
logService.recordFailure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", bill.getStatus(),
|
||||
e.getMessage(), operator, requestId);
|
||||
throw new ServiceException("账单导出失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ArBillExport> selectExports(Long arBillId)
|
||||
{
|
||||
return exportMapper.selectByBillId(arBillId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadExport(Long exportId, HttpServletResponse response) throws Exception
|
||||
{
|
||||
ArBillExport export = exportMapper.selectById(exportId);
|
||||
if (export == null || !ArExportStatus.SUCCESS.getCode().equals(export.getExportStatus()) || StringUtils.isEmpty(export.getStorageKey()))
|
||||
{
|
||||
throw new ServiceException("导出文件不存在或尚未生成成功");
|
||||
}
|
||||
String resource = export.getStorageKey();
|
||||
if (!FileUtils.checkAllowDownload(resource))
|
||||
{
|
||||
throw new ServiceException("导出文件路径非法");
|
||||
}
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
FileUtils.setAttachmentResponseHeader(response, export.getFileName());
|
||||
FileUtils.writeBytes(RuoYiConfig.getProfile() + FileUtils.stripPrefix(resource), response.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ArBillLog> selectLogList(ArBillLog query)
|
||||
{
|
||||
return logService.selectLogList(query);
|
||||
}
|
||||
|
||||
private void fillDetails(ArBill bill)
|
||||
{
|
||||
List<ArBillDetail> details = detailMapper.selectByBillIdWithFees(bill.getArBillId());
|
||||
for (ArBillDetail detail : details)
|
||||
{
|
||||
if (detail.getFees() == null)
|
||||
{
|
||||
detail.setFees(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
bill.setDetails(details);
|
||||
}
|
||||
|
||||
private boolean validateDetails(Long arBillId, BigDecimal billTotal)
|
||||
{
|
||||
List<ArBillDetail> details = detailMapper.selectByBillIdWithFees(arBillId);
|
||||
BigDecimal detailTotal = BigDecimal.ZERO;
|
||||
for (ArBillDetail detail : details)
|
||||
{
|
||||
BigDecimal feeTotal = BigDecimal.ZERO;
|
||||
List<ArBillDetailFee> fees = detail.getFees();
|
||||
if (fees == null)
|
||||
{
|
||||
fees = new ArrayList<ArBillDetailFee>();
|
||||
}
|
||||
for (ArBillDetailFee fee : fees)
|
||||
{
|
||||
feeTotal = feeTotal.add(nvl(fee.getAmount()));
|
||||
}
|
||||
if (feeTotal.compareTo(nvl(detail.getTotalAmount())) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
detailTotal = detailTotal.add(nvl(detail.getTotalAmount()));
|
||||
}
|
||||
return !details.isEmpty() && detailTotal.compareTo(nvl(billTotal)) == 0;
|
||||
}
|
||||
|
||||
private ArBillDetail toDetail(ArBill bill, PrcOrderBill source, int detailNo, String operator)
|
||||
{
|
||||
ArBillDetail detail = new ArBillDetail();
|
||||
detail.setArBillId(bill.getArBillId());
|
||||
detail.setPrcBillId(source.getBillId());
|
||||
detail.setDetailNo(detailNo);
|
||||
detail.setOrderNo(source.getOrderNo());
|
||||
detail.setTrackingNo(source.getTrackingNo());
|
||||
detail.setReceiveTime(source.getOrderTime());
|
||||
detail.setProductCode(source.getProductCode());
|
||||
detail.setProductName(source.getProductName());
|
||||
detail.setGoodsDescription(source.getOrderRemark());
|
||||
detail.setCountryCode(source.getCountryCode());
|
||||
detail.setCountryName(source.getCountryName());
|
||||
detail.setDestPostcode(source.getDestPostcode());
|
||||
detail.setActualWeight(source.getActualWeight());
|
||||
detail.setVolumeWeight(source.getVolumeWeight());
|
||||
detail.setChargeWeight(source.getChargeWeight() == null ? source.getWeight() : source.getChargeWeight());
|
||||
detail.setPkgLength(source.getPkgLength());
|
||||
detail.setPkgWidth(source.getPkgWidth());
|
||||
detail.setPkgHeight(source.getPkgHeight());
|
||||
detail.setCurrency(source.getCurrency() == null ? bill.getCurrency() : source.getCurrency());
|
||||
detail.setTotalAmount(nvl(source.getIncomeTotal()));
|
||||
detail.setRemark(source.getOrderRemark());
|
||||
detail.setCreateBy(operator);
|
||||
return detail;
|
||||
}
|
||||
|
||||
private ArBillDetailFee toDetailFee(ArBillDetail detail, PrcIncomeBillFee source, int feeNo, String operator)
|
||||
{
|
||||
ArBillDetailFee fee = new ArBillDetailFee();
|
||||
fee.setDetailId(detail.getDetailId());
|
||||
fee.setPrcFeeId(source.getFeeId());
|
||||
fee.setFeeNo(feeNo);
|
||||
fee.setFeeType(source.getFeeType());
|
||||
fee.setFeeName(source.getFeeName());
|
||||
fee.setScId(source.getScId());
|
||||
fee.setAmount(nvl(source.getAmount()));
|
||||
fee.setCurrency(source.getCurrency() == null ? detail.getCurrency() : source.getCurrency());
|
||||
fee.setRoundMode(source.getRoundMode());
|
||||
fee.setRemark(source.getRemark());
|
||||
fee.setCreateBy(operator);
|
||||
return fee;
|
||||
}
|
||||
|
||||
private void recordSuccess(ArBill bill, String operationType, String desc, String fromStatus,
|
||||
String toStatus, String requestId, String operator)
|
||||
{
|
||||
recordSuccess(bill, operationType, desc, fromStatus, toStatus, requestId, operator, null);
|
||||
}
|
||||
|
||||
private void recordSuccess(ArBill bill, String operationType, String desc, String fromStatus,
|
||||
String toStatus, String requestId, String operator, Long objectId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(bill.getArBillId());
|
||||
log.setObjectType("BILL".equals(operationType) ? "BILL" : operationType.equals("EXPORT") ? "EXPORT" : "BILL");
|
||||
log.setObjectId(objectId == null ? String.valueOf(bill.getArBillId()) : String.valueOf(objectId));
|
||||
log.setOperationType(operationType);
|
||||
log.setOperationDesc(desc);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setToStatus(toStatus);
|
||||
log.setBeforeData(safeStatusJson(fromStatus));
|
||||
log.setAfterData(safeStatusJson(toStatus));
|
||||
log.setOperatorId(operator);
|
||||
log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB");
|
||||
log.setRequestId(requestId);
|
||||
log.setResult(ArLogResult.SUCCESS.getCode());
|
||||
logService.record(log);
|
||||
}
|
||||
|
||||
private ServiceException failure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
String fromStatus, String message, String operator, String requestId)
|
||||
{
|
||||
logService.recordFailure(arBillId, objectType, objectId, operationType, fromStatus, message, operator, requestId);
|
||||
return new ServiceException(message);
|
||||
}
|
||||
|
||||
private byte[] buildWorkbook(ArBill bill) throws IOException
|
||||
{
|
||||
ClassPathResource resource = new ClassPathResource(EXPORT_TEMPLATE_RESOURCE);
|
||||
if (!resource.exists())
|
||||
{
|
||||
throw new IOException("应收账单导出模板不存在: " + EXPORT_TEMPLATE_RESOURCE);
|
||||
}
|
||||
try (InputStream input = resource.getInputStream(); XSSFWorkbook workbook = new XSSFWorkbook(input))
|
||||
{
|
||||
Sheet sheet = workbook.getSheet("8.31");
|
||||
if (sheet == null)
|
||||
{
|
||||
sheet = workbook.getSheetAt(0);
|
||||
}
|
||||
|
||||
Row templateDataRow = sheet.getRow(TEMPLATE_DATA_START_ROW);
|
||||
List<ArBillDetail> details = bill.getDetails() == null ? new ArrayList<ArBillDetail>() : bill.getDetails();
|
||||
CellStyle bodyStyle = createTemplateBodyStyle(workbook);
|
||||
int footerStartRow = TEMPLATE_FOOTER_START_ROW + Math.max(details.size() - 1, 0);
|
||||
int footerShift = footerStartRow - TEMPLATE_FOOTER_START_ROW;
|
||||
if (footerShift != 0)
|
||||
{
|
||||
sheet.shiftRows(TEMPLATE_FOOTER_START_ROW, sheet.getLastRowNum(), footerShift);
|
||||
}
|
||||
|
||||
set(sheet, 0, 0, StringUtils.isEmpty(bill.getBankAccountName()) ? "威狮国际供应链(深圳)有限公司" : bill.getBankAccountName());
|
||||
set(sheet, 2, 2, bill.getCustomerName());
|
||||
set(sheet, 2, 15, exportPeriod(bill));
|
||||
|
||||
for (int i = 0; i < details.size(); i++)
|
||||
{
|
||||
Row row = sheet.getRow(TEMPLATE_DATA_START_ROW + i);
|
||||
if (row == null)
|
||||
{
|
||||
row = sheet.createRow(TEMPLATE_DATA_START_ROW + i);
|
||||
copyRowStyle(templateDataRow, row);
|
||||
}
|
||||
writeTemplateDetail(row, details.get(i), bodyStyle);
|
||||
}
|
||||
|
||||
int bankRow = footerStartRow;
|
||||
set(sheet, bankRow, 1, "银行信息");
|
||||
set(sheet, bankRow + 1, 1, "开户名");
|
||||
set(sheet, bankRow + 1, 2, bill.getBankAccountName());
|
||||
set(sheet, bankRow + 2, 1, "账号");
|
||||
set(sheet, bankRow + 2, 2, exportBankAccountNo(bill.getBankAccountNo()));
|
||||
set(sheet, bankRow + 3, 1, "开户行");
|
||||
set(sheet, bankRow + 3, 2, bill.getBankName());
|
||||
|
||||
set(sheet, bankRow, 15, "本期应收金额");
|
||||
set(sheet, bankRow, 18, bill.getTotalAmount());
|
||||
set(sheet, bankRow + 1, 15, "本期已核销金额");
|
||||
set(sheet, bankRow + 1, 18, bill.getWrittenOffAmount());
|
||||
set(sheet, bankRow + 2, 15, "本期未核销金额");
|
||||
set(sheet, bankRow + 2, 18, bill.getUnwrittenAmount());
|
||||
return toBytes(workbook);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] toBytes(XSSFWorkbook workbook) throws IOException
|
||||
{
|
||||
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
|
||||
workbook.write(out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private void set(Row row, int index, Object value)
|
||||
{
|
||||
Cell cell = row.getCell(index, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
|
||||
cell.setCellValue("");
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (value instanceof Number)
|
||||
{
|
||||
cell.setCellValue(((Number) value).doubleValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.setCellValue(String.valueOf(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void set(Sheet sheet, int rowIndex, int columnIndex, Object value)
|
||||
{
|
||||
Row row = sheet.getRow(rowIndex);
|
||||
if (row == null)
|
||||
{
|
||||
row = sheet.createRow(rowIndex);
|
||||
}
|
||||
set(row, columnIndex, value);
|
||||
}
|
||||
|
||||
private void copyRowStyle(Row source, Row target)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
target.setHeight(source.getHeight());
|
||||
for (int index = 0; index < TEMPLATE_COLUMN_COUNT; index++)
|
||||
{
|
||||
Cell sourceCell = source.getCell(index);
|
||||
if (sourceCell != null)
|
||||
{
|
||||
target.getCell(index, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellStyle(sourceCell.getCellStyle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CellStyle createTemplateBodyStyle(XSSFWorkbook workbook)
|
||||
{
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setFontName("微软雅黑");
|
||||
font.setFontHeightInPoints((short) 10);
|
||||
style.setFont(font);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
style.setWrapText(true);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
|
||||
private void writeTemplateDetail(Row row, ArBillDetail detail, CellStyle bodyStyle)
|
||||
{
|
||||
row.setHeightInPoints(24);
|
||||
Object[] values = {
|
||||
detail.getDetailNo(),
|
||||
detail.getReceiveTime() == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(detail.getReceiveTime()),
|
||||
detail.getOrderNo(), detail.getTrackingNo(),
|
||||
StringUtils.isEmpty(detail.getProductName()) ? detail.getProductCode() : detail.getProductName(),
|
||||
StringUtils.isEmpty(detail.getCountryName()) ? detail.getCountryCode() : detail.getCountryName(),
|
||||
detail.getDestPostcode(), decimal(detail.getActualWeight(), 3), decimal(detail.getPkgLength(), 2),
|
||||
decimal(detail.getPkgHeight(), 2), decimal(detail.getPkgWidth(), 2), decimal(detail.getVolumeWeight(), 3),
|
||||
decimal(detail.getChargeWeight(), 3), exportUnitPrice(detail), exportProcessingFee(detail),
|
||||
exportFormula(detail), exportFeeTypes(detail), detail.getCurrency(), detail.getTotalAmount(), exportRemark(detail)
|
||||
};
|
||||
for (int index = 0; index < values.length; index++)
|
||||
{
|
||||
set(row, index, values[index]);
|
||||
row.getCell(index, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellStyle(bodyStyle);
|
||||
}
|
||||
}
|
||||
|
||||
private String exportRemark(ArBillDetail detail)
|
||||
{
|
||||
return StringUtils.isEmpty(detail.getRemark()) ? detail.getGoodsDescription() : detail.getRemark();
|
||||
}
|
||||
|
||||
private String exportPeriod(ArBill bill)
|
||||
{
|
||||
if (bill.getBillPeriodEnd() == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return "截至 " + new SimpleDateFormat("yyyy-MM-dd").format(bill.getBillPeriodEnd());
|
||||
}
|
||||
|
||||
private BigDecimal exportUnitPrice(ArBillDetail detail)
|
||||
{
|
||||
BigDecimal direct = firstFeeUnitPrice(detail.getFees());
|
||||
if (direct != null)
|
||||
{
|
||||
return direct;
|
||||
}
|
||||
BigDecimal freight = feeAmount(detail, "BASIC_FREIGHT");
|
||||
if (freight == null || detail.getChargeWeight() == null || detail.getChargeWeight().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return freight.divide(detail.getChargeWeight(), 4, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal firstFeeUnitPrice(List<ArBillDetailFee> fees)
|
||||
{
|
||||
if (fees == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (ArBillDetailFee fee : fees)
|
||||
{
|
||||
if (fee.getUnitPrice() != null)
|
||||
{
|
||||
return fee.getUnitPrice();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BigDecimal exportProcessingFee(ArBillDetail detail)
|
||||
{
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
boolean found = false;
|
||||
if (detail.getFees() != null)
|
||||
{
|
||||
for (ArBillDetailFee fee : detail.getFees())
|
||||
{
|
||||
String name = fee.getFeeName() == null ? "" : fee.getFeeName();
|
||||
if ("REGISTRATION_FEE".equalsIgnoreCase(fee.getFeeType()) || name.contains("处理费"))
|
||||
{
|
||||
total = total.add(nvl(fee.getAmount()));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found ? total : null;
|
||||
}
|
||||
|
||||
private String exportFormula(ArBillDetail detail)
|
||||
{
|
||||
String formula = joinFeeText(detail.getFees(), true);
|
||||
if (!StringUtils.isEmpty(formula))
|
||||
{
|
||||
return formula;
|
||||
}
|
||||
boolean freight = feeAmount(detail, "BASIC_FREIGHT") != null;
|
||||
boolean processing = exportProcessingFee(detail) != null;
|
||||
boolean surcharge = hasFeeType(detail.getFees(), "SURCHARGE");
|
||||
if (!freight)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return "结算重量*公斤价" + (processing ? "+处理费" : "") + (surcharge ? "+附加费" : "");
|
||||
}
|
||||
|
||||
private String exportFeeTypes(ArBillDetail detail)
|
||||
{
|
||||
return joinFeeText(detail.getFees(), false);
|
||||
}
|
||||
|
||||
private String joinFeeText(List<ArBillDetailFee> fees, boolean formula)
|
||||
{
|
||||
if (fees == null || fees.isEmpty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (ArBillDetailFee fee : fees)
|
||||
{
|
||||
String value = formula ? fee.getCalculationFormula() : (StringUtils.isEmpty(fee.getFeeName()) ? fee.getFeeType() : fee.getFeeName());
|
||||
if (StringUtils.isEmpty(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (result.length() > 0)
|
||||
{
|
||||
result.append("、");
|
||||
}
|
||||
result.append(value);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private BigDecimal feeAmount(ArBillDetail detail, String feeType)
|
||||
{
|
||||
if (detail.getFees() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
boolean found = false;
|
||||
for (ArBillDetailFee fee : detail.getFees())
|
||||
{
|
||||
if (feeType.equalsIgnoreCase(fee.getFeeType()))
|
||||
{
|
||||
total = total.add(nvl(fee.getAmount()));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found ? total : null;
|
||||
}
|
||||
|
||||
private boolean hasFeeType(List<ArBillDetailFee> fees, String feeType)
|
||||
{
|
||||
if (fees == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (ArBillDetailFee fee : fees)
|
||||
{
|
||||
if (feeType.equalsIgnoreCase(fee.getFeeType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String exportBankAccountNo(String value)
|
||||
{
|
||||
if (StringUtils.isEmpty(value))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
String normalized = value.replaceAll("\\s+", "");
|
||||
StringBuilder result = new StringBuilder(normalized.length() + normalized.length() / 4);
|
||||
for (int index = 0; index < normalized.length(); index++)
|
||||
{
|
||||
if (index > 0 && index % 4 == 0)
|
||||
{
|
||||
result.append(' ');
|
||||
}
|
||||
result.append(normalized.charAt(index));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private BigDecimal decimal(BigDecimal value, int scale) { return value == null ? null : value.setScale(scale, java.math.RoundingMode.HALF_UP); }
|
||||
private BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; }
|
||||
private String safeFilePart(String value) { return StringUtils.isEmpty(value) ? "客户" : value.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5_-]", "_"); }
|
||||
private String maskAccount(String value) { if (StringUtils.isEmpty(value)) return ""; return value.length() <= 4 ? "****" : "****" + value.substring(value.length() - 4); }
|
||||
private String sha256(byte[] data) throws NoSuchAlgorithmException { byte[] digest = MessageDigest.getInstance("SHA-256").digest(data); StringBuilder sb = new StringBuilder(); for (byte b : digest) sb.append(String.format("%02x", b)); return sb.toString(); }
|
||||
private boolean isNumber(String value) { if (StringUtils.isEmpty(value)) return false; try { Long.valueOf(value); return true; } catch (NumberFormatException e) { return false; } }
|
||||
private String safeStatusJson(String status) { Map<String, String> data = new HashMap<>(); data.put("status", status); return JSON.toJSONString(data); }
|
||||
private void validatePeriod(ArBillGenerateRequest request)
|
||||
{
|
||||
if (request == null || request.getCustomerId() == null || StringUtils.isEmpty(request.getBillName())
|
||||
|| request.getBillName().trim().isEmpty()
|
||||
|| request.getBillPeriodEnd() == null || request.getSourceBillIds() == null || request.getSourceBillIds().isEmpty())
|
||||
{
|
||||
throw new ServiceException("客户、账单名称、账单截止日和订单选择不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePreviewQuery(ArBillGenerateQuery query)
|
||||
{
|
||||
if (query == null || query.getCustomerId() == null || query.getBillPeriodEnd() == null)
|
||||
{
|
||||
throw new ServiceException("客户和账单截止日不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private Date earliestBizDate(List<PrcOrderBill> sources)
|
||||
{
|
||||
Date earliest = null;
|
||||
for (PrcOrderBill source : sources)
|
||||
{
|
||||
if (source.getBizDate() != null && (earliest == null || source.getBizDate().before(earliest)))
|
||||
{
|
||||
earliest = source.getBizDate();
|
||||
}
|
||||
}
|
||||
return earliest == null ? new Date() : earliest;
|
||||
}
|
||||
|
||||
private List<String> normalizeSourceBillIds(List<String> sourceBillIds)
|
||||
{
|
||||
LinkedHashSet<String> uniqueIds = new LinkedHashSet<>();
|
||||
if (sourceBillIds != null)
|
||||
{
|
||||
for (String billId : sourceBillIds)
|
||||
{
|
||||
if (!StringUtils.isEmpty(billId) && billId.trim().length() <= 36)
|
||||
{
|
||||
uniqueIds.add(billId.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(uniqueIds);
|
||||
}
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
package com.ruoyi.ar.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.domain.ArReceipt;
|
||||
import com.ruoyi.ar.domain.ArReceiptWriteoff;
|
||||
import com.ruoyi.ar.domain.vo.ArReceiptQuery;
|
||||
import com.ruoyi.ar.domain.vo.ArReceiptWriteoffRequest;
|
||||
import com.ruoyi.ar.enums.ArBillStatus;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.enums.ArReceiptStatus;
|
||||
import com.ruoyi.ar.enums.ArWriteoffStatus;
|
||||
import com.ruoyi.ar.mapper.ArBillMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillLogMapper;
|
||||
import com.ruoyi.ar.mapper.ArReceiptMapper;
|
||||
import com.ruoyi.ar.mapper.ArReceiptWriteoffMapper;
|
||||
import com.ruoyi.ar.service.IArBillLogService;
|
||||
import com.ruoyi.ar.service.IArReceiptService;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
|
||||
@Service
|
||||
public class ArReceiptServiceImpl implements IArReceiptService
|
||||
{
|
||||
@Autowired private ArReceiptMapper receiptMapper;
|
||||
@Autowired private ArReceiptWriteoffMapper writeoffMapper;
|
||||
@Autowired private ArBillMapper billMapper;
|
||||
@Autowired private ArBillLogMapper logMapper;
|
||||
@Autowired private IArBillLogService logService;
|
||||
|
||||
@Override
|
||||
public List<ArReceipt> selectReceiptList(ArReceiptQuery query)
|
||||
{
|
||||
return receiptMapper.selectArReceiptList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ArReceiptWriteoff> selectWriteoffs(Long receiptId)
|
||||
{
|
||||
return writeoffMapper.selectByReceiptId(receiptId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ArReceipt registerReceipt(ArReceipt receipt, String operator, String requestId)
|
||||
{
|
||||
if (receipt == null || receipt.getCustomerId() == null || receipt.getReceiptDate() == null
|
||||
|| receipt.getReceiptAmount() == null || receipt.getReceiptAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw failure(null, "RECEIPT", "REGISTER", "收款客户、日期和正数金额不能为空", operator, requestId);
|
||||
}
|
||||
if (StringUtils.isEmpty(receipt.getReceiptNo()))
|
||||
{
|
||||
receipt.setReceiptNo("RC" + DateUtils.dateTimeNow("yyyyMMddHHmmss") + IdUtils.fastSimpleUUID().substring(0, 6).toUpperCase());
|
||||
}
|
||||
if (StringUtils.isEmpty(receipt.getCurrency())) receipt.setCurrency("CNY");
|
||||
if (StringUtils.isEmpty(receipt.getPaymentMethod())) receipt.setPaymentMethod("BANK");
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "RECEIPT");
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
ArReceipt existed = receiptMapper.selectArReceiptById(Long.valueOf(previous.getObjectId()));
|
||||
if (existed != null) return existed;
|
||||
}
|
||||
if (receiptMapper.selectArReceiptByNo(receipt.getReceiptNo()) != null)
|
||||
{
|
||||
throw failure(null, "RECEIPT", receipt.getReceiptNo(), "收款单号已存在", operator, requestId);
|
||||
}
|
||||
if (!StringUtils.isEmpty(receipt.getBankReference())
|
||||
&& receiptMapper.selectArReceiptByReference(receipt.getPaymentMethod(), receipt.getBankReference()) != null)
|
||||
{
|
||||
throw failure(null, "RECEIPT", receipt.getReceiptNo(), "银行流水号已存在", operator, requestId);
|
||||
}
|
||||
receipt.setUnallocatedAmount(receipt.getReceiptAmount());
|
||||
receipt.setStatus(ArReceiptStatus.REGISTERED.getCode());
|
||||
receipt.setCreateBy(operator);
|
||||
receipt.setUpdateBy(operator);
|
||||
try
|
||||
{
|
||||
receiptMapper.insertArReceipt(receipt);
|
||||
}
|
||||
catch (DuplicateKeyException e)
|
||||
{
|
||||
ArReceipt existed = receiptMapper.selectArReceiptByNo(receipt.getReceiptNo());
|
||||
if (existed != null) return existed;
|
||||
throw e;
|
||||
}
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setObjectType("RECEIPT"); log.setObjectId(String.valueOf(receipt.getReceiptId()));
|
||||
log.setOperationType("RECEIPT"); log.setOperationDesc("登记应收收款");
|
||||
log.setOperatorId(operator); log.setOperatorName(operator); log.setOperationSource("WEB");
|
||||
log.setRequestId(requestId); log.setResult(ArLogResult.SUCCESS.getCode()); logService.record(log);
|
||||
return receipt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelReceipt(Long receiptId, String remark, String operator, String requestId)
|
||||
{
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "RECEIPT");
|
||||
if (previous != null && "CANCEL_RECEIPT".equals(previous.getOperationType())) return;
|
||||
ArReceipt receipt = receiptMapper.selectArReceiptByIdForUpdate(receiptId);
|
||||
if (receipt == null) throw failure(null, "RECEIPT", String.valueOf(receiptId), "收款不存在", operator, requestId);
|
||||
if (!ArReceiptStatus.REGISTERED.getCode().equals(receipt.getStatus())
|
||||
|| nvl(receipt.getUnallocatedAmount()).compareTo(nvl(receipt.getReceiptAmount())) != 0)
|
||||
{
|
||||
throw failure(null, "RECEIPT", String.valueOf(receiptId), "收款已发生核销,不能直接取消", operator, requestId);
|
||||
}
|
||||
receiptMapper.updateCancelled(receiptId, operator, StringUtils.isEmpty(remark) ? "取消收款" : remark);
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setObjectType("RECEIPT"); log.setObjectId(String.valueOf(receiptId)); log.setOperationType("CANCEL_RECEIPT");
|
||||
log.setOperationDesc(StringUtils.isEmpty(remark) ? "取消收款" : remark); log.setFromStatus(ArReceiptStatus.REGISTERED.getCode());
|
||||
log.setToStatus(ArReceiptStatus.CANCELLED.getCode()); log.setOperatorId(operator); log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB"); log.setRequestId(requestId); log.setResult(ArLogResult.SUCCESS.getCode()); logService.record(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<ArReceiptWriteoff> writeoff(ArReceiptWriteoffRequest request, String operator, String requestId)
|
||||
{
|
||||
if (request == null || request.getReceiptId() == null || request.getAllocations() == null || request.getAllocations().isEmpty())
|
||||
{
|
||||
throw failure(null, "WRITEOFF", "", "核销收款和账单不能为空", operator, requestId);
|
||||
}
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "WRITEOFF");
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
ArReceiptWriteoff existed = writeoffMapper.selectById(Long.valueOf(previous.getObjectId()));
|
||||
if (existed != null) return writeoffMapper.selectByReceiptId(existed.getReceiptId());
|
||||
}
|
||||
ArReceipt receipt = receiptMapper.selectArReceiptByIdForUpdate(request.getReceiptId());
|
||||
if (receipt == null) throw failure(null, "WRITEOFF", String.valueOf(request.getReceiptId()), "收款不存在", operator, requestId);
|
||||
if (ArReceiptStatus.CANCELLED.getCode().equals(receipt.getStatus()))
|
||||
{
|
||||
throw failure(null, "WRITEOFF", String.valueOf(request.getReceiptId()), "收款已取消,不允许核销", operator, requestId);
|
||||
}
|
||||
String currency = StringUtils.isEmpty(request.getCurrency()) ? receipt.getCurrency() : request.getCurrency();
|
||||
if (!currency.equalsIgnoreCase(receipt.getCurrency()))
|
||||
{
|
||||
throw failure(null, "WRITEOFF", String.valueOf(request.getReceiptId()), "收款与核销币种不一致", operator, requestId);
|
||||
}
|
||||
BigDecimal allocationTotal = BigDecimal.ZERO;
|
||||
for (ArReceiptWriteoffRequest.Allocation allocation : request.getAllocations())
|
||||
{
|
||||
if (allocation.getAmount() == null || allocation.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw failure(null, "WRITEOFF", String.valueOf(request.getReceiptId()), "核销金额必须大于零", operator, requestId);
|
||||
}
|
||||
allocationTotal = allocationTotal.add(allocation.getAmount());
|
||||
}
|
||||
if (allocationTotal.compareTo(nvl(receipt.getUnallocatedAmount())) > 0)
|
||||
{
|
||||
throw failure(null, "WRITEOFF", String.valueOf(request.getReceiptId()), "核销金额超过收款未分配余额", operator, requestId);
|
||||
}
|
||||
|
||||
List<ArReceiptWriteoff> result = new ArrayList<>();
|
||||
BigDecimal remainingReceipt = nvl(receipt.getUnallocatedAmount());
|
||||
Date writeoffDate = request.getWriteoffDate() == null ? new Date() : request.getWriteoffDate();
|
||||
for (ArReceiptWriteoffRequest.Allocation allocation : request.getAllocations())
|
||||
{
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(allocation.getArBillId());
|
||||
if (bill == null) throw failure(null, "WRITEOFF", String.valueOf(allocation.getArBillId()), "账单不存在", operator, requestId);
|
||||
ArBillStatus billStatus = ArBillStatus.of(bill.getStatus());
|
||||
if (billStatus == ArBillStatus.VOID) throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "账单已作废,不允许核销", operator, requestId);
|
||||
if (billStatus == null || !billStatus.canWriteOff())
|
||||
{
|
||||
throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "账单尚未确认,不允许核销", operator, requestId);
|
||||
}
|
||||
if (!currency.equalsIgnoreCase(bill.getCurrency())) throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "收款与账单币种不一致", operator, requestId);
|
||||
BigDecimal amount = allocation.getAmount();
|
||||
if (amount.compareTo(nvl(bill.getUnwrittenAmount())) > 0)
|
||||
{
|
||||
throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "核销金额超过账单未核销余额", operator, requestId);
|
||||
}
|
||||
ArReceiptWriteoff writeoff = new ArReceiptWriteoff();
|
||||
writeoff.setReceiptId(receipt.getReceiptId()); writeoff.setArBillId(bill.getArBillId());
|
||||
writeoff.setWriteoffNo("WO" + DateUtils.dateTimeNow("yyyyMMddHHmmss") + IdUtils.fastSimpleUUID().substring(0, 6).toUpperCase());
|
||||
writeoff.setWriteoffAmount(amount); writeoff.setCurrency(currency); writeoff.setWriteoffDate(writeoffDate);
|
||||
writeoff.setStatus(ArWriteoffStatus.EFFECTIVE.getCode()); writeoff.setRemark(request.getRemark()); writeoff.setCreateBy(operator);
|
||||
writeoffMapper.insert(writeoff);
|
||||
remainingReceipt = remainingReceipt.subtract(amount);
|
||||
BigDecimal written = nvl(bill.getWrittenOffAmount()).add(amount);
|
||||
BigDecimal unwritten = nvl(bill.getUnwrittenAmount()).subtract(amount);
|
||||
String nextBillStatus = billStatus(written, nvl(bill.getTotalAmount()), bill.getStatus());
|
||||
if (billMapper.updateArBillAmounts(bill.getArBillId(), written, unwritten,
|
||||
nextBillStatus, operator, bill.getStatus()) != 1)
|
||||
{
|
||||
throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
writeoffLog(writeoff, bill, operator, requestId);
|
||||
result.add(writeoff);
|
||||
}
|
||||
receiptMapper.updateBalanceAndStatus(receipt.getReceiptId(), remainingReceipt,
|
||||
remainingReceipt.compareTo(BigDecimal.ZERO) == 0 ? ArReceiptStatus.WRITTEN_OFF.getCode() : ArReceiptStatus.PARTIAL_WRITTEN_OFF.getCode(), operator);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelWriteoff(Long writeoffId, String remark, String operator, String requestId)
|
||||
{
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "WRITEOFF");
|
||||
if (previous != null && "CANCEL_WRITEOFF".equals(previous.getOperationType())) return;
|
||||
ArReceiptWriteoff writeoff = writeoffMapper.selectByIdForUpdate(writeoffId);
|
||||
if (writeoff == null) throw failure(null, "WRITEOFF", String.valueOf(writeoffId), "核销记录不存在", operator, requestId);
|
||||
if (!ArWriteoffStatus.EFFECTIVE.getCode().equals(writeoff.getStatus())) throw failure(writeoff.getArBillId(), "WRITEOFF", String.valueOf(writeoffId), "只有有效核销可以取消", operator, requestId);
|
||||
ArReceipt receipt = receiptMapper.selectArReceiptByIdForUpdate(writeoff.getReceiptId());
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(writeoff.getArBillId());
|
||||
if (receipt == null || bill == null) throw failure(writeoff.getArBillId(), "WRITEOFF", String.valueOf(writeoffId), "核销关联的收款或账单不存在", operator, requestId);
|
||||
writeoffMapper.updateCancelled(writeoffId, operator, StringUtils.isEmpty(remark) ? "取消核销" : remark);
|
||||
BigDecimal receiptUnallocated = nvl(receipt.getUnallocatedAmount()).add(writeoff.getWriteoffAmount());
|
||||
receiptMapper.updateBalanceAndStatus(receipt.getReceiptId(), receiptUnallocated,
|
||||
receiptUnallocated.compareTo(nvl(receipt.getReceiptAmount())) == 0 ? ArReceiptStatus.REGISTERED.getCode() : ArReceiptStatus.PARTIAL_WRITTEN_OFF.getCode(), operator);
|
||||
BigDecimal written = nvl(bill.getWrittenOffAmount()).subtract(writeoff.getWriteoffAmount());
|
||||
if (written.compareTo(BigDecimal.ZERO) < 0) written = BigDecimal.ZERO;
|
||||
BigDecimal unwritten = nvl(bill.getTotalAmount()).subtract(written);
|
||||
String nextBillStatus = billStatus(written, nvl(bill.getTotalAmount()), bill.getStatus());
|
||||
if (billMapper.updateArBillAmounts(bill.getArBillId(), written, unwritten,
|
||||
nextBillStatus, operator, bill.getStatus()) != 1)
|
||||
{
|
||||
throw failure(bill.getArBillId(), "WRITEOFF", String.valueOf(bill.getArBillId()), "账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(bill.getArBillId()); log.setObjectType("WRITEOFF"); log.setObjectId(String.valueOf(writeoffId));
|
||||
log.setOperationType("CANCEL_WRITEOFF"); log.setOperationDesc(StringUtils.isEmpty(remark) ? "取消核销" : remark);
|
||||
log.setFromStatus(ArWriteoffStatus.EFFECTIVE.getCode()); log.setToStatus(ArWriteoffStatus.CANCELLED.getCode()); log.setOperatorId(operator); log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB"); log.setRequestId(requestId); log.setResult(ArLogResult.SUCCESS.getCode()); logService.record(log);
|
||||
}
|
||||
|
||||
private void writeoffLog(ArReceiptWriteoff writeoff, ArBill bill, String operator, String requestId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(bill.getArBillId()); log.setObjectType("WRITEOFF"); log.setObjectId(String.valueOf(writeoff.getWriteoffId()));
|
||||
log.setOperationType("WRITEOFF"); log.setOperationDesc("收款核销到账单"); log.setFromStatus(bill.getStatus());
|
||||
log.setToStatus(billStatus(nvl(bill.getWrittenOffAmount()).add(writeoff.getWriteoffAmount()), nvl(bill.getTotalAmount()), bill.getStatus()));
|
||||
log.setOperatorId(operator); log.setOperatorName(operator); log.setOperationSource("WEB"); log.setRequestId(requestId); log.setResult(ArLogResult.SUCCESS.getCode());
|
||||
logService.record(log);
|
||||
}
|
||||
|
||||
private String billStatus(BigDecimal written, BigDecimal total, String current)
|
||||
{
|
||||
if (written.compareTo(BigDecimal.ZERO) == 0)
|
||||
{
|
||||
return ArBillStatus.DRAFT.getCode().equals(current)
|
||||
? ArBillStatus.DRAFT.getCode() : ArBillStatus.CONFIRMED.getCode();
|
||||
}
|
||||
return written.compareTo(total) >= 0
|
||||
? ArBillStatus.WRITTEN_OFF.getCode() : ArBillStatus.PARTIAL_WRITTEN_OFF.getCode();
|
||||
}
|
||||
|
||||
private ServiceException failure(Long billId, String objectType, String message, String operator, String requestId)
|
||||
{
|
||||
logService.recordFailure(billId, objectType, objectType, "RECEIPT", null, message, operator, requestId);
|
||||
return new ServiceException(message);
|
||||
}
|
||||
|
||||
private ServiceException failure(Long billId, String objectType, String objectId, String message, String operator, String requestId)
|
||||
{
|
||||
logService.recordFailure(billId, objectType, objectId,
|
||||
"RECEIPT".equals(objectType) ? "RECEIPT" : "WRITEOFF", null, message, operator, requestId);
|
||||
return new ServiceException(message);
|
||||
}
|
||||
|
||||
private BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; }
|
||||
private boolean isNumber(String value) { if (StringUtils.isEmpty(value)) return false; try { Long.valueOf(value); return true; } catch (NumberFormatException e) { return false; } }
|
||||
}
|
||||
@@ -71,6 +71,8 @@ public class PrcOrderBill extends BaseEntity
|
||||
/** payable total */
|
||||
private BigDecimal payTotal;
|
||||
private String currency;
|
||||
/** 应收账单占用状态:UNBILLED/BILLED。 */
|
||||
private String arBillStatus;
|
||||
private String roundMode;
|
||||
|
||||
private List<PrcIncomeBillFee> incomeFees;
|
||||
|
||||
@@ -9,6 +9,8 @@ public interface PrcIncomeBillFeeMapper
|
||||
{
|
||||
public List<PrcIncomeBillFee> selectByBillId(String billId);
|
||||
|
||||
public List<PrcIncomeBillFee> selectByBillIds(@Param("billIds") List<String> billIds);
|
||||
|
||||
public int insertPrcIncomeBillFee(PrcIncomeBillFee fee);
|
||||
|
||||
public int insertBatch(@Param("list") List<PrcIncomeBillFee> fees);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.price.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.price.domain.PrcOrderBill;
|
||||
@@ -14,6 +15,25 @@ public interface PrcOrderBillMapper
|
||||
|
||||
public List<PrcOrderBill> selectPrcOrderBillList(PrcOrderBill bill);
|
||||
|
||||
/** 应收账单生成读取指定客户截止日期前的未出账订单计费快照。 */
|
||||
public List<PrcOrderBill> selectPrcOrderBillByCustomerPeriod(@Param("customerId") Long customerId,
|
||||
@Param("customerCode") String customerCode, @Param("cutoffDate") Date cutoffDate,
|
||||
@Param("currency") String currency, @Param("arBillStatus") String arBillStatus);
|
||||
|
||||
/** 按生成账单时用户勾选的订单读取,并再次校验账单筛选条件。 */
|
||||
public List<PrcOrderBill> selectPrcOrderBillByCustomerCutoffAndIds(@Param("customerId") Long customerId,
|
||||
@Param("customerCode") String customerCode, @Param("cutoffDate") Date cutoffDate,
|
||||
@Param("currency") String currency, @Param("billIds") List<String> billIds,
|
||||
@Param("arBillStatus") String arBillStatus);
|
||||
|
||||
/** 原子占用订单,防止同一订单被并发生成到多张应收账单。 */
|
||||
public int claimArBillOrders(@Param("billIds") List<String> billIds, @Param("operator") String operator,
|
||||
@Param("fromStatus") String fromStatus, @Param("toStatus") String toStatus);
|
||||
|
||||
/** 作废账单后释放订单,允许后续重新生成新版本账单。 */
|
||||
public int releaseArBillOrders(@Param("billIds") List<String> billIds, @Param("operator") String operator,
|
||||
@Param("fromStatus") String fromStatus, @Param("toStatus") String toStatus);
|
||||
|
||||
public int insertPrcOrderBill(PrcOrderBill bill);
|
||||
|
||||
public int updatePrcOrderBill(PrcOrderBill bill);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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.ruoyi.ar.mapper.ArBankAccountMapper">
|
||||
<resultMap id="ArBankAccountResult" type="ArBankAccount">
|
||||
<id property="bankAccountId" column="bank_account_id"/>
|
||||
<result property="accountName" column="account_name"/>
|
||||
<result property="accountNo" column="account_no"/>
|
||||
<result property="bankName" column="bank_name"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="isDefault" column="is_default"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectDefaultByCurrency" resultMap="ArBankAccountResult">
|
||||
select bank_account_id, account_name, account_no, bank_name, currency,
|
||||
is_default, status, remark, create_by, create_time, update_by, update_time
|
||||
from ar_bank_account
|
||||
where currency = #{currency}
|
||||
and status = '0'
|
||||
order by is_default desc, bank_account_id
|
||||
limit 1
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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.ruoyi.ar.mapper.ArBillDetailFeeMapper">
|
||||
<resultMap id="ArBillDetailFeeResult" type="ArBillDetailFee">
|
||||
<id property="detailFeeId" column="detail_fee_id"/>
|
||||
<result property="detailId" column="detail_id"/>
|
||||
<result property="prcFeeId" column="prc_fee_id"/>
|
||||
<result property="feeNo" column="fee_no"/>
|
||||
<result property="feeType" column="fee_type"/>
|
||||
<result property="feeName" column="fee_name"/>
|
||||
<result property="scId" column="sc_id"/>
|
||||
<result property="quantity" column="quantity"/>
|
||||
<result property="unit" column="unit"/>
|
||||
<result property="unitPrice" column="unit_price"/>
|
||||
<result property="amount" column="amount"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="calculationFormula" column="calculation_formula"/>
|
||||
<result property="roundMode" column="round_mode"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByDetailId" parameterType="long" resultMap="ArBillDetailFeeResult">
|
||||
select detail_fee_id, detail_id, prc_fee_id, fee_no, fee_type, fee_name, sc_id, quantity,
|
||||
unit, unit_price, amount, currency, calculation_formula, round_mode, remark, create_by, create_time
|
||||
from ar_bill_detail_fee where detail_id = #{detailId} order by fee_no, detail_fee_id
|
||||
</select>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArBillDetailFeeResult">
|
||||
select detail_fee_id, detail_id, prc_fee_id, fee_no, fee_type, fee_name, sc_id, quantity,
|
||||
unit, unit_price, amount, currency, calculation_formula, round_mode, remark, create_by, create_time
|
||||
from ar_bill_detail_fee
|
||||
where detail_id in (select detail_id from ar_bill_detail where ar_bill_id = #{arBillId})
|
||||
order by detail_id, fee_no, detail_fee_id
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArBillDetailFee" useGeneratedKeys="true" keyProperty="detailFeeId">
|
||||
insert into ar_bill_detail_fee (
|
||||
detail_id, prc_fee_id, fee_no, fee_type, fee_name, sc_id, quantity, unit, unit_price,
|
||||
amount, currency, calculation_formula, round_mode, remark, create_by, create_time
|
||||
) values (
|
||||
#{detailId}, #{prcFeeId}, #{feeNo}, #{feeType}, #{feeName}, #{scId}, #{quantity}, #{unit},
|
||||
#{unitPrice}, #{amount}, #{currency}, #{calculationFormula}, #{roundMode}, #{remark},
|
||||
#{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="detailFeeId">
|
||||
insert into ar_bill_detail_fee (
|
||||
detail_id, prc_fee_id, fee_no, fee_type, fee_name, sc_id, quantity, unit, unit_price,
|
||||
amount, currency, calculation_formula, round_mode, remark, create_by, create_time
|
||||
) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.detailId}, #{item.prcFeeId}, #{item.feeNo}, #{item.feeType}, #{item.feeName}, #{item.scId},
|
||||
#{item.quantity}, #{item.unit}, #{item.unitPrice}, #{item.amount}, #{item.currency},
|
||||
#{item.calculationFormula}, #{item.roundMode}, #{item.remark}, #{item.createBy}, sysdate())
|
||||
</foreach>
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?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.ruoyi.ar.mapper.ArBillDetailMapper">
|
||||
<resultMap id="ArBillDetailResult" type="ArBillDetail">
|
||||
<id property="detailId" column="detail_id"/>
|
||||
<result property="arBillId" column="ar_bill_id"/>
|
||||
<result property="prcBillId" column="prc_bill_id"/>
|
||||
<result property="detailNo" column="detail_no"/>
|
||||
<result property="orderNo" column="order_no"/>
|
||||
<result property="trackingNo" column="tracking_no"/>
|
||||
<result property="receiveTime" column="receive_time"/>
|
||||
<result property="productCode" column="product_code"/>
|
||||
<result property="productName" column="product_name"/>
|
||||
<result property="goodsDescription" column="goods_description"/>
|
||||
<result property="countryCode" column="country_code"/>
|
||||
<result property="countryName" column="country_name"/>
|
||||
<result property="destPostcode" column="dest_postcode"/>
|
||||
<result property="actualWeight" column="actual_weight"/>
|
||||
<result property="volumeWeight" column="volume_weight"/>
|
||||
<result property="chargeWeight" column="charge_weight"/>
|
||||
<result property="pkgLength" column="pkg_length"/>
|
||||
<result property="pkgWidth" column="pkg_width"/>
|
||||
<result property="pkgHeight" column="pkg_height"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="totalAmount" column="total_amount"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_bill_id, detail_no, order_no, tracking_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight, volume_weight, charge_weight, pkg_length, pkg_width, pkg_height,
|
||||
currency, total_amount, remark, create_by, create_time
|
||||
from ar_bill_detail where ar_bill_id = #{arBillId} order by detail_no
|
||||
</select>
|
||||
|
||||
<resultMap id="ArBillDetailWithFeesResult" type="ArBillDetail" extends="ArBillDetailResult">
|
||||
<collection property="fees" ofType="ArBillDetailFee">
|
||||
<id property="detailFeeId" column="fee_detail_id"/>
|
||||
<result property="detailId" column="fee_parent_detail_id"/>
|
||||
<result property="prcFeeId" column="fee_prc_fee_id"/>
|
||||
<result property="feeNo" column="fee_no"/>
|
||||
<result property="feeType" column="fee_type"/>
|
||||
<result property="feeName" column="fee_name"/>
|
||||
<result property="scId" column="fee_sc_id"/>
|
||||
<result property="quantity" column="fee_quantity"/>
|
||||
<result property="unit" column="fee_unit"/>
|
||||
<result property="unitPrice" column="fee_unit_price"/>
|
||||
<result property="amount" column="fee_amount"/>
|
||||
<result property="currency" column="fee_currency"/>
|
||||
<result property="calculationFormula" column="fee_calculation_formula"/>
|
||||
<result property="roundMode" column="fee_round_mode"/>
|
||||
<result property="remark" column="fee_remark"/>
|
||||
<result property="createBy" column="fee_create_by"/>
|
||||
<result property="createTime" column="fee_create_time"/>
|
||||
</collection>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByBillIdWithFees" parameterType="long" resultMap="ArBillDetailWithFeesResult">
|
||||
select d.detail_id, d.ar_bill_id, d.prc_bill_id, d.detail_no, d.order_no, d.tracking_no, d.receive_time,
|
||||
d.product_code, d.product_name, d.goods_description, d.country_code, d.country_name, d.dest_postcode,
|
||||
d.actual_weight, d.volume_weight, d.charge_weight, d.pkg_length, d.pkg_width, d.pkg_height,
|
||||
d.currency, d.total_amount, d.remark, d.create_by, d.create_time,
|
||||
f.detail_fee_id as fee_detail_id, f.detail_id as fee_parent_detail_id, f.prc_fee_id as fee_prc_fee_id,
|
||||
f.fee_no, f.fee_type, f.fee_name, f.sc_id as fee_sc_id, f.quantity as fee_quantity,
|
||||
f.unit as fee_unit, f.unit_price as fee_unit_price, f.amount as fee_amount,
|
||||
f.currency as fee_currency, f.calculation_formula as fee_calculation_formula,
|
||||
f.round_mode as fee_round_mode, f.remark as fee_remark, f.create_by as fee_create_by,
|
||||
f.create_time as fee_create_time
|
||||
from ar_bill_detail d
|
||||
left join ar_bill_detail_fee f on f.detail_id = d.detail_id
|
||||
where d.ar_bill_id = #{arBillId}
|
||||
order by d.detail_no, d.detail_id, f.fee_no, f.detail_fee_id
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArBillDetail" useGeneratedKeys="true" keyProperty="detailId">
|
||||
insert into ar_bill_detail (
|
||||
ar_bill_id, prc_bill_id, detail_no, order_no, tracking_no, receive_time, product_code,
|
||||
product_name, goods_description, country_code, country_name, dest_postcode, actual_weight,
|
||||
volume_weight, charge_weight, pkg_length, pkg_width, pkg_height, currency, total_amount,
|
||||
remark, create_by, create_time
|
||||
) values (
|
||||
#{arBillId}, #{prcBillId}, #{detailNo}, #{orderNo}, #{trackingNo}, #{receiveTime},
|
||||
#{productCode}, #{productName}, #{goodsDescription}, #{countryCode}, #{countryName},
|
||||
#{destPostcode}, #{actualWeight}, #{volumeWeight}, #{chargeWeight}, #{pkgLength}, #{pkgWidth},
|
||||
#{pkgHeight}, #{currency}, #{totalAmount}, #{remark}, #{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="detailId">
|
||||
insert into ar_bill_detail (
|
||||
ar_bill_id, prc_bill_id, detail_no, order_no, tracking_no, receive_time, product_code,
|
||||
product_name, goods_description, country_code, country_name, dest_postcode, actual_weight,
|
||||
volume_weight, charge_weight, pkg_length, pkg_width, pkg_height, currency, total_amount,
|
||||
remark, create_by, create_time
|
||||
) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.arBillId}, #{item.prcBillId}, #{item.detailNo}, #{item.orderNo}, #{item.trackingNo},
|
||||
#{item.receiveTime}, #{item.productCode}, #{item.productName}, #{item.goodsDescription},
|
||||
#{item.countryCode}, #{item.countryName}, #{item.destPostcode}, #{item.actualWeight},
|
||||
#{item.volumeWeight}, #{item.chargeWeight}, #{item.pkgLength}, #{item.pkgWidth}, #{item.pkgHeight},
|
||||
#{item.currency}, #{item.totalAmount}, #{item.remark}, #{item.createBy}, sysdate())
|
||||
</foreach>
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?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.ruoyi.ar.mapper.ArBillExportMapper">
|
||||
<resultMap id="ArBillExportResult" type="ArBillExport">
|
||||
<id property="exportId" column="export_id"/>
|
||||
<result property="arBillId" column="ar_bill_id"/>
|
||||
<result property="fileName" column="file_name"/>
|
||||
<result property="fileType" column="file_type"/>
|
||||
<result property="templateCode" column="template_code"/>
|
||||
<result property="templateVersion" column="template_version"/>
|
||||
<result property="storageKey" column="storage_key"/>
|
||||
<result property="fileSize" column="file_size"/>
|
||||
<result property="fileHash" column="file_hash"/>
|
||||
<result property="rowCount" column="row_count"/>
|
||||
<result property="exportStatus" column="export_status"/>
|
||||
<result property="exportBy" column="export_by"/>
|
||||
<result property="exportTime" column="export_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectExportColumns">
|
||||
select export_id, ar_bill_id, file_name, file_type, template_code, template_version,
|
||||
storage_key, file_size, file_hash, row_count, export_status, export_by, export_time, remark
|
||||
from ar_bill_export
|
||||
</sql>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArBillExportResult">
|
||||
<include refid="selectExportColumns"/> where ar_bill_id = #{arBillId} order by export_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectById" parameterType="long" resultMap="ArBillExportResult">
|
||||
<include refid="selectExportColumns"/> where export_id = #{exportId}
|
||||
</select>
|
||||
|
||||
<select id="selectLatestSuccessByBillId" parameterType="long" resultMap="ArBillExportResult">
|
||||
<include refid="selectExportColumns"/>
|
||||
where ar_bill_id = #{arBillId} and export_status = 'SUCCESS'
|
||||
order by export_time desc limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArBillExport" useGeneratedKeys="true" keyProperty="exportId">
|
||||
insert into ar_bill_export (
|
||||
ar_bill_id, file_name, file_type, template_code, template_version, storage_key,
|
||||
file_size, file_hash, row_count, export_status, export_by, export_time, remark
|
||||
) values (
|
||||
#{arBillId}, #{fileName}, #{fileType}, #{templateCode}, #{templateVersion}, #{storageKey},
|
||||
#{fileSize}, #{fileHash}, #{rowCount}, #{exportStatus}, #{exportBy}, sysdate(), #{remark}
|
||||
)
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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.ruoyi.ar.mapper.ArBillLogMapper">
|
||||
<resultMap id="ArBillLogResult" type="ArBillLog">
|
||||
<id property="logId" column="log_id"/>
|
||||
<result property="arBillId" column="ar_bill_id"/>
|
||||
<result property="objectType" column="object_type"/>
|
||||
<result property="objectId" column="object_id"/>
|
||||
<result property="objectDisplay" column="object_display"/>
|
||||
<result property="operationType" column="operation_type"/>
|
||||
<result property="operationDesc" column="operation_desc"/>
|
||||
<result property="fromStatus" column="from_status"/>
|
||||
<result property="toStatus" column="to_status"/>
|
||||
<result property="beforeData" column="before_data"/>
|
||||
<result property="afterData" column="after_data"/>
|
||||
<result property="operatorId" column="operator_id"/>
|
||||
<result property="operatorName" column="operator_name"/>
|
||||
<result property="operationSource" column="operation_source"/>
|
||||
<result property="requestId" column="request_id"/>
|
||||
<result property="traceId" column="trace_id"/>
|
||||
<result property="result" column="result"/>
|
||||
<result property="errorMessage" column="error_message"/>
|
||||
<result property="clientIp" column="client_ip"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectList" parameterType="ArBillLog" resultMap="ArBillLogResult">
|
||||
select l.log_id, l.ar_bill_id, l.object_type, l.object_id, l.operation_type, l.operation_desc,
|
||||
case l.object_type
|
||||
when 'BILL' then b.bill_no
|
||||
when 'EXPORT' then coalesce(e.file_name, b.bill_no)
|
||||
when 'RECEIPT' then r.receipt_no
|
||||
when 'WRITEOFF' then w.writeoff_no
|
||||
else null
|
||||
end as object_display,
|
||||
l.from_status, l.to_status, l.before_data, l.after_data, l.operator_id, l.operator_name,
|
||||
l.operation_source, l.request_id, l.trace_id, l.result, l.error_message, l.client_ip, l.create_time
|
||||
from ar_bill_log l
|
||||
left join ar_bill b on b.ar_bill_id = l.ar_bill_id
|
||||
left join ar_bill_export e on e.export_id = case when l.object_type = 'EXPORT' then cast(l.object_id as unsigned) end
|
||||
left join ar_receipt r on r.receipt_id = case when l.object_type = 'RECEIPT' then cast(l.object_id as unsigned) end
|
||||
left join ar_receipt_writeoff w on w.writeoff_id = case when l.object_type = 'WRITEOFF' then cast(l.object_id as unsigned) end
|
||||
<where>
|
||||
<if test="arBillId != null">and l.ar_bill_id = #{arBillId}</if>
|
||||
<if test="objectType != null and objectType != ''">and l.object_type = #{objectType}</if>
|
||||
<if test="objectId != null and objectId != ''">and l.object_id = #{objectId}</if>
|
||||
<if test="operationType != null and operationType != ''">and l.operation_type = #{operationType}</if>
|
||||
<if test="requestId != null and requestId != ''">and l.request_id = #{requestId}</if>
|
||||
<if test="result != null and result != ''">and l.result = #{result}</if>
|
||||
</where>
|
||||
order by l.create_time desc, l.log_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectSuccessByRequestId" resultMap="ArBillLogResult">
|
||||
select log_id, ar_bill_id, object_type, object_id, operation_type, operation_desc,
|
||||
from_status, to_status, before_data, after_data, operator_id, operator_name,
|
||||
operation_source, request_id, trace_id, result, error_message, client_ip, create_time
|
||||
from ar_bill_log
|
||||
where request_id = #{requestId} and object_type = #{objectType} and result = 'SUCCESS'
|
||||
order by log_id limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArBillLog" useGeneratedKeys="true" keyProperty="logId">
|
||||
insert into ar_bill_log (
|
||||
ar_bill_id, object_type, object_id, operation_type, operation_desc, from_status, to_status,
|
||||
before_data, after_data, operator_id, operator_name, operation_source, request_id,
|
||||
trace_id, result, error_message, client_ip, create_time
|
||||
) values (
|
||||
#{arBillId}, #{objectType}, #{objectId}, #{operationType}, #{operationDesc}, #{fromStatus}, #{toStatus},
|
||||
#{beforeData}, #{afterData}, #{operatorId}, #{operatorName}, #{operationSource}, #{requestId},
|
||||
#{traceId}, #{result}, #{errorMessage}, #{clientIp}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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.ruoyi.ar.mapper.ArBillMapper">
|
||||
<resultMap id="ArBillResult" type="ArBill">
|
||||
<id property="arBillId" column="ar_bill_id"/>
|
||||
<result property="billNo" column="bill_no"/>
|
||||
<result property="billName" column="bill_name"/>
|
||||
<result property="customerId" column="customer_id"/>
|
||||
<result property="customerCode" column="customer_code"/>
|
||||
<result property="customerName" column="customer_name"/>
|
||||
<result property="billPeriodStart" column="bill_period_start"/>
|
||||
<result property="billPeriodEnd" column="bill_period_end"/>
|
||||
<result property="billDate" column="bill_date"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="orderCount" column="order_count"/>
|
||||
<result property="totalAmount" column="total_amount"/>
|
||||
<result property="writtenOffAmount" column="written_off_amount"/>
|
||||
<result property="unwrittenAmount" column="unwritten_amount"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="billVersion" column="bill_version"/>
|
||||
<result property="bankAccountName" column="bank_account_name"/>
|
||||
<result property="bankAccountNo" column="bank_account_no"/>
|
||||
<result property="bankName" column="bank_name"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="confirmedBy" column="confirmed_by"/>
|
||||
<result property="confirmedTime" column="confirmed_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectArBillColumns">
|
||||
select ar_bill_id, bill_no, bill_name, customer_id, customer_code, customer_name,
|
||||
bill_period_start, bill_period_end, bill_date, currency, order_count,
|
||||
total_amount, written_off_amount, unwritten_amount, status, bill_version,
|
||||
bank_account_name, bank_account_no, bank_name, remark, confirmed_by,
|
||||
confirmed_time, create_by, create_time, update_by, update_time
|
||||
from ar_bill
|
||||
</sql>
|
||||
|
||||
<select id="selectArBillList" parameterType="ArBill" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/>
|
||||
<where>
|
||||
<if test="billNo != null and billNo != ''">and bill_no like concat('%', #{billNo}, '%')</if>
|
||||
<if test="billName != null and billName != ''">and bill_name like concat('%', #{billName}, '%')</if>
|
||||
<if test="customerCode != null and customerCode != ''">and customer_code like concat('%', #{customerCode}, '%')</if>
|
||||
<if test="customerName != null and customerName != ''">and customer_name like concat('%', #{customerName}, '%')</if>
|
||||
<if test="customerId != null">and customer_id = #{customerId}</if>
|
||||
<if test="status != null and status != ''">and status = #{status}</if>
|
||||
<if test="currency != null and currency != ''">and currency = #{currency}</if>
|
||||
<if test="billPeriodStart != null">and bill_period_end >= #{billPeriodStart}</if>
|
||||
<if test="billPeriodEnd != null">and bill_period_start <= #{billPeriodEnd}</if>
|
||||
</where>
|
||||
order by create_time desc, ar_bill_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectArBillById" parameterType="long" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/> where ar_bill_id = #{arBillId}
|
||||
</select>
|
||||
|
||||
<select id="selectArBillByBillNo" parameterType="string" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/> where bill_no = #{billNo}
|
||||
</select>
|
||||
|
||||
<select id="selectMaxBillVersion" resultType="int">
|
||||
select bill_version
|
||||
from ar_bill
|
||||
where customer_id = #{customerId}
|
||||
and bill_period_end = #{cutoffDate}
|
||||
and currency = #{currency}
|
||||
order by bill_version desc
|
||||
limit 1
|
||||
for update
|
||||
</select>
|
||||
|
||||
<select id="selectArBillByIdForUpdate" parameterType="long" resultMap="ArBillResult">
|
||||
<include refid="selectArBillColumns"/> where ar_bill_id = #{arBillId} for update
|
||||
</select>
|
||||
|
||||
<insert id="insertArBill" parameterType="ArBill" useGeneratedKeys="true" keyProperty="arBillId">
|
||||
insert into ar_bill (
|
||||
bill_no, bill_name, customer_id, customer_code, customer_name, bill_period_start, bill_period_end,
|
||||
bill_date, currency, order_count, total_amount, written_off_amount, unwritten_amount,
|
||||
status, bill_version, bank_account_name, bank_account_no, bank_name, remark,
|
||||
confirmed_by, confirmed_time, create_by, create_time, update_by, update_time
|
||||
) values (
|
||||
#{billNo}, #{billName}, #{customerId}, #{customerCode}, #{customerName}, #{billPeriodStart}, #{billPeriodEnd},
|
||||
#{billDate}, #{currency}, #{orderCount}, #{totalAmount}, #{writtenOffAmount}, #{unwrittenAmount},
|
||||
#{status}, #{billVersion}, #{bankAccountName}, #{bankAccountNo}, #{bankName}, #{remark},
|
||||
#{confirmedBy}, #{confirmedTime}, #{createBy}, sysdate(), #{updateBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateArBillStatus">
|
||||
update ar_bill
|
||||
set status = #{status}, remark = #{remark}, update_by = #{operator}, update_time = sysdate(),
|
||||
confirmed_by = case when #{status} = 'CONFIRMED' then #{operator} else confirmed_by end,
|
||||
confirmed_time = case when #{status} = 'CONFIRMED' then sysdate() else confirmed_time end
|
||||
where ar_bill_id = #{arBillId}
|
||||
and status = #{fromStatus}
|
||||
</update>
|
||||
|
||||
<update id="updateArBillAmounts">
|
||||
update ar_bill
|
||||
set written_off_amount = #{writtenOffAmount}, unwritten_amount = #{unwrittenAmount},
|
||||
status = #{status}, update_by = #{operator}, update_time = sysdate()
|
||||
where ar_bill_id = #{arBillId}
|
||||
and status = #{fromStatus}
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -0,0 +1,97 @@
|
||||
<?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.ruoyi.ar.mapper.ArReceiptMapper">
|
||||
<resultMap id="ArReceiptResult" type="ArReceipt">
|
||||
<id property="receiptId" column="receipt_id"/>
|
||||
<result property="receiptNo" column="receipt_no"/>
|
||||
<result property="customerId" column="customer_id"/>
|
||||
<result property="customerName" column="customer_name"/>
|
||||
<result property="customerCode" column="customer_code"/>
|
||||
<result property="receiptDate" column="receipt_date"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="receiptAmount" column="receipt_amount"/>
|
||||
<result property="unallocatedAmount" column="unallocated_amount"/>
|
||||
<result property="paymentMethod" column="payment_method"/>
|
||||
<result property="bankReference" column="bank_reference"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectReceiptColumns">
|
||||
select r.receipt_id, r.receipt_no, r.customer_id, c.customer_name, c.customer_code,
|
||||
r.receipt_date, r.currency, r.receipt_amount, r.unallocated_amount,
|
||||
r.payment_method, r.bank_reference, r.status, r.remark,
|
||||
r.create_by, r.create_time, r.update_by, r.update_time
|
||||
from ar_receipt r
|
||||
left join bms_customer c on c.customer_id = r.customer_id and c.del_flag = '0'
|
||||
</sql>
|
||||
|
||||
<!-- 加锁更新只读取收款主表,避免锁定展示关联表。 -->
|
||||
<sql id="selectReceiptLockColumns">
|
||||
select receipt_id, receipt_no, customer_id, receipt_date, currency, receipt_amount,
|
||||
unallocated_amount, payment_method, bank_reference, status, remark,
|
||||
create_by, create_time, update_by, update_time
|
||||
from ar_receipt
|
||||
</sql>
|
||||
|
||||
<select id="selectArReceiptList" parameterType="com.ruoyi.ar.domain.vo.ArReceiptQuery" resultMap="ArReceiptResult">
|
||||
<include refid="selectReceiptColumns"/>
|
||||
<where>
|
||||
<if test="receiptNo != null and receiptNo != ''">and r.receipt_no like concat('%', #{receiptNo}, '%')</if>
|
||||
<if test="customerId != null">and r.customer_id = #{customerId}</if>
|
||||
<if test="currency != null and currency != ''">and r.currency = #{currency}</if>
|
||||
<if test="paymentMethod != null and paymentMethod != ''">and r.payment_method = #{paymentMethod}</if>
|
||||
<if test="status != null and status != ''">and r.status = #{status}</if>
|
||||
<if test="receiptDateStart != null">and r.receipt_date >= #{receiptDateStart}</if>
|
||||
<if test="receiptDateEnd != null">and r.receipt_date <= #{receiptDateEnd}</if>
|
||||
</where>
|
||||
order by r.receipt_date desc, r.receipt_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectArReceiptById" parameterType="long" resultMap="ArReceiptResult">
|
||||
<include refid="selectReceiptColumns"/> where r.receipt_id = #{receiptId}
|
||||
</select>
|
||||
|
||||
<select id="selectArReceiptByIdForUpdate" parameterType="long" resultMap="ArReceiptResult">
|
||||
<include refid="selectReceiptLockColumns"/> where receipt_id = #{receiptId} for update
|
||||
</select>
|
||||
|
||||
<select id="selectArReceiptByNo" parameterType="string" resultMap="ArReceiptResult">
|
||||
<include refid="selectReceiptColumns"/> where r.receipt_no = #{receiptNo}
|
||||
</select>
|
||||
|
||||
<select id="selectArReceiptByReference" resultMap="ArReceiptResult">
|
||||
<include refid="selectReceiptColumns"/>
|
||||
where r.payment_method = #{paymentMethod} and r.bank_reference = #{bankReference}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insertArReceipt" parameterType="ArReceipt" useGeneratedKeys="true" keyProperty="receiptId">
|
||||
insert into ar_receipt (
|
||||
receipt_no, customer_id, receipt_date, currency, receipt_amount, unallocated_amount,
|
||||
payment_method, bank_reference, status, remark, create_by, create_time, update_by, update_time
|
||||
) values (
|
||||
#{receiptNo}, #{customerId}, #{receiptDate}, #{currency}, #{receiptAmount}, #{unallocatedAmount},
|
||||
#{paymentMethod}, #{bankReference}, #{status}, #{remark}, #{createBy}, sysdate(), #{updateBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateBalanceAndStatus">
|
||||
update ar_receipt
|
||||
set unallocated_amount = #{unallocatedAmount}, status = #{status},
|
||||
update_by = #{operator}, update_time = sysdate()
|
||||
where receipt_id = #{receiptId}
|
||||
</update>
|
||||
|
||||
<update id="updateCancelled">
|
||||
update ar_receipt set status = 'CANCELLED', remark = #{remark},
|
||||
update_by = #{operator}, update_time = sysdate()
|
||||
where receipt_id = #{receiptId}
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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.ruoyi.ar.mapper.ArReceiptWriteoffMapper">
|
||||
<resultMap id="ArReceiptWriteoffResult" type="ArReceiptWriteoff">
|
||||
<id property="writeoffId" column="writeoff_id"/>
|
||||
<result property="receiptId" column="receipt_id"/>
|
||||
<result property="arBillId" column="ar_bill_id"/>
|
||||
<result property="billNo" column="bill_no"/>
|
||||
<result property="receiptNo" column="receipt_no"/>
|
||||
<result property="writeoffNo" column="writeoff_no"/>
|
||||
<result property="writeoffAmount" column="writeoff_amount"/>
|
||||
<result property="currency" column="currency"/>
|
||||
<result property="writeoffDate" column="writeoff_date"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="cancelBy" column="cancel_by"/>
|
||||
<result property="cancelTime" column="cancel_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectWriteoffColumns">
|
||||
select w.writeoff_id, w.receipt_id, w.ar_bill_id, b.bill_no, r.receipt_no,
|
||||
w.writeoff_no, w.writeoff_amount, w.currency, w.writeoff_date, w.status,
|
||||
w.remark, w.create_by, w.create_time, w.cancel_by, w.cancel_time
|
||||
from ar_receipt_writeoff w
|
||||
left join ar_bill b on b.ar_bill_id = w.ar_bill_id
|
||||
left join ar_receipt r on r.receipt_id = w.receipt_id
|
||||
</sql>
|
||||
|
||||
<!-- 加锁更新只读取核销关系主表,展示字段不参与锁定。 -->
|
||||
<sql id="selectWriteoffLockColumns">
|
||||
select writeoff_id, receipt_id, ar_bill_id, writeoff_no, writeoff_amount, currency,
|
||||
writeoff_date, status, remark, create_by, create_time, cancel_by, cancel_time
|
||||
from ar_receipt_writeoff
|
||||
</sql>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArReceiptWriteoffResult">
|
||||
<include refid="selectWriteoffColumns"/> where w.ar_bill_id = #{arBillId} order by w.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectByReceiptId" parameterType="long" resultMap="ArReceiptWriteoffResult">
|
||||
<include refid="selectWriteoffColumns"/> where w.receipt_id = #{receiptId} order by w.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectById" parameterType="long" resultMap="ArReceiptWriteoffResult">
|
||||
<include refid="selectWriteoffColumns"/> where w.writeoff_id = #{writeoffId}
|
||||
</select>
|
||||
|
||||
<select id="selectByIdForUpdate" parameterType="long" resultMap="ArReceiptWriteoffResult">
|
||||
<include refid="selectWriteoffLockColumns"/> where writeoff_id = #{writeoffId} for update
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArReceiptWriteoff" useGeneratedKeys="true" keyProperty="writeoffId">
|
||||
insert into ar_receipt_writeoff (
|
||||
receipt_id, ar_bill_id, writeoff_no, writeoff_amount, currency, writeoff_date,
|
||||
status, remark, create_by, create_time
|
||||
) values (
|
||||
#{receiptId}, #{arBillId}, #{writeoffNo}, #{writeoffAmount}, #{currency}, #{writeoffDate},
|
||||
#{status}, #{remark}, #{createBy}, sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateCancelled">
|
||||
update ar_receipt_writeoff
|
||||
set status = 'CANCELLED', remark = #{remark}, cancel_by = #{cancelBy}, cancel_time = sysdate()
|
||||
where writeoff_id = #{writeoffId} and status = 'EFFECTIVE'
|
||||
</update>
|
||||
|
||||
<select id="sumEffectiveByBillId" parameterType="long" resultType="java.math.BigDecimal">
|
||||
select coalesce(sum(writeoff_amount), 0) from ar_receipt_writeoff
|
||||
where ar_bill_id = #{arBillId} and status = 'EFFECTIVE'
|
||||
</select>
|
||||
|
||||
<select id="sumEffectiveByReceiptId" parameterType="long" resultType="java.math.BigDecimal">
|
||||
select coalesce(sum(writeoff_amount), 0) from ar_receipt_writeoff
|
||||
where receipt_id = #{receiptId} and status = 'EFFECTIVE'
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -24,6 +24,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
order by field(fee_type, 'BASIC_FREIGHT', 'REGISTRATION_FEE', 'SURCHARGE'), fee_id
|
||||
</select>
|
||||
|
||||
<select id="selectByBillIds" resultMap="PrcIncomeBillFeeResult">
|
||||
select fee_id, bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, create_time
|
||||
from prc_income_bill_fee
|
||||
where bill_id in
|
||||
<foreach collection="billIds" item="billId" open="(" separator="," close=")">
|
||||
#{billId}
|
||||
</foreach>
|
||||
order by bill_id, field(fee_type, 'BASIC_FREIGHT', 'REGISTRATION_FEE', 'SURCHARGE'), fee_id
|
||||
</select>
|
||||
|
||||
<insert id="insertPrcIncomeBillFee" parameterType="PrcIncomeBillFee">
|
||||
insert into prc_income_bill_fee (
|
||||
fee_id, bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, create_time
|
||||
|
||||
@@ -28,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="productName" column="product_name" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="weight" column="weight" />
|
||||
<result property="bizDate" column="biz_date" />
|
||||
<result property="tmsId" column="tms_id" />
|
||||
@@ -55,6 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="incomeTotal" column="income_total" />
|
||||
<result property="payTotal" column="pay_total" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="arBillStatus" column="ar_bill_status" />
|
||||
<result property="roundMode" column="round_mode" />
|
||||
<result property="createBy" column="created_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@@ -71,7 +72,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
set_type, bind_priority, customer_priceset_id, product_country_id,
|
||||
weight_min, weight_max, weight_label, effective_date, charge_weight,
|
||||
freight_rate, base_freight_rate, discount_percent, registration_fee,
|
||||
freight_amount, surcharge_amount, income_total, pay_total, currency, round_mode,
|
||||
freight_amount, surcharge_amount, income_total, pay_total, currency, ar_bill_status, round_mode,
|
||||
created_by, create_time, updated_by, update_time
|
||||
from prc_order_bill
|
||||
</sql>
|
||||
@@ -123,6 +124,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
order by create_time desc, bill_id desc
|
||||
</select>
|
||||
|
||||
<!-- The repository's rename_bill_tables.sql maps the requirement's prc_bill to prc_order_bill. -->
|
||||
<select id="selectPrcOrderBillByCustomerPeriod" resultMap="PrcOrderBillResult">
|
||||
<include refid="selectPrcOrderBillVo"/>
|
||||
where customer_id = #{customerId}
|
||||
<if test="customerCode != null and customerCode != ''">
|
||||
and customer_code like concat('%', #{customerCode}, '%')
|
||||
</if>
|
||||
and biz_date <= #{cutoffDate}
|
||||
and ar_bill_status = #{arBillStatus}
|
||||
<if test="currency != null and currency != ''">
|
||||
and currency = #{currency}
|
||||
</if>
|
||||
order by biz_date, order_no, bill_id
|
||||
</select>
|
||||
|
||||
<select id="selectPrcOrderBillByCustomerCutoffAndIds" resultMap="PrcOrderBillResult">
|
||||
<include refid="selectPrcOrderBillVo"/>
|
||||
where customer_id = #{customerId}
|
||||
and biz_date <= #{cutoffDate}
|
||||
and ar_bill_status = #{arBillStatus}
|
||||
and bill_id in
|
||||
<foreach collection="billIds" item="billId" open="(" separator="," close=")">
|
||||
#{billId}
|
||||
</foreach>
|
||||
<if test="customerCode != null and customerCode != ''">
|
||||
and customer_code like concat('%', #{customerCode}, '%')
|
||||
</if>
|
||||
<if test="currency != null and currency != ''">
|
||||
and currency = #{currency}
|
||||
</if>
|
||||
order by biz_date, order_no, bill_id
|
||||
</select>
|
||||
|
||||
<insert id="insertPrcOrderBill" parameterType="PrcOrderBill">
|
||||
insert into prc_order_bill (
|
||||
bill_id, bill_key, order_no, tracking_no, customer_id, customer_code, customer_name,
|
||||
@@ -172,7 +206,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
product_code = #{productCode},
|
||||
product_name = #{productName},
|
||||
country_code = #{countryCode},
|
||||
country_name = #{countryName},
|
||||
country_name = #{countryName},
|
||||
|
||||
|
||||
weight = #{weight},
|
||||
biz_date = #{bizDate},
|
||||
tms_id = #{tmsId},
|
||||
@@ -212,4 +248,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
update_time = sysdate()
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="claimArBillOrders">
|
||||
update prc_order_bill
|
||||
set ar_bill_status = #{toStatus}, updated_by = #{operator}, update_time = sysdate()
|
||||
where ar_bill_status = #{fromStatus}
|
||||
and bill_id in
|
||||
<foreach collection="billIds" item="billId" open="(" separator="," close=")">
|
||||
#{billId}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<update id="releaseArBillOrders">
|
||||
update prc_order_bill
|
||||
set ar_bill_status = #{toStatus}, updated_by = #{operator}, update_time = sysdate()
|
||||
where ar_bill_status = #{fromStatus}
|
||||
and bill_id in
|
||||
<foreach collection="billIds" item="billId" open="(" separator="," close=")">
|
||||
#{billId}
|
||||
</foreach>
|
||||
</update>
|
||||
</mapper>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user