去掉应收登记收款功能逻辑

This commit is contained in:
caojia
2026-09-16 17:57:34 +08:00
parent d2ed7fd4ea
commit e52f52cb35
22 changed files with 30 additions and 878 deletions
@@ -10,8 +10,8 @@ import lombok.EqualsAndHashCode;
/**
* 应收账单余额核销 ar_balance_writeoff
*
* <p>用客户账户余额核销应收账单,与 ar_receipt_writeoff(收款核销)并列,
* 两者共享 ar_bill.written_off_amount / unwritten_amount 缓存。</p>
* <p>用客户账户余额核销应收账单,和客户账户余额流水联动,
* 共享 ar_bill.written_off_amount / unwritten_amount 缓存。</p>
*
* @author ruoyi
* @date 2026-09-16
@@ -1,33 +0,0 @@
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;
}
@@ -1,35 +0,0 @@
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;
}
@@ -1,16 +0,0 @@
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;
}
@@ -1,35 +0,0 @@
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;
}
}
@@ -1,22 +0,0 @@
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;
}
}
@@ -1,20 +0,0 @@
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;
}
}
@@ -1,30 +0,0 @@
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);
}
@@ -1,26 +0,0 @@
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);
}
@@ -7,8 +7,7 @@ import com.ruoyi.ar.domain.ArBill;
/**
* 应收账单余额核销 Service接口
*
* <p>资金来源为客户账户余额(customer_account.balance),与收款核销
* (ar_receipt_writeoff)并列;核销时三方联动:账户余额扣减、已用额度释放、
* <p>资金来源为客户账户余额(customer_account.balance);核销时三方联动:账户余额扣减、已用额度释放、
* 账单已核销/未核销金额及状态更新。</p>
*
* @author ruoyi
@@ -1,22 +0,0 @@
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);
}
@@ -1,275 +0,0 @@
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; } }
}
@@ -31,8 +31,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
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
when 'BALANCE_WRITEOFF' then bw.writeoff_no
else null
end as object_display,
@@ -41,8 +39,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
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
left join ar_balance_writeoff bw on bw.writeoff_id = case when l.object_type = 'BALANCE_WRITEOFF' then cast(l.object_id as unsigned) end
<where>
<if test="arBillId != null">and l.ar_bill_id = #{arBillId}</if>
@@ -1,97 +0,0 @@
<?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 &gt;= #{receiptDateStart}</if>
<if test="receiptDateEnd != null">and r.receipt_date &lt;= #{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>
@@ -1,81 +0,0 @@
<?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>