From d2ed7fd4eac96920289b16fd4ee7a1c3ebcabd6b Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Wed, 16 Sep 2026 17:25:41 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=BA=94=E6=94=B6=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/mapper/ar/ArBillLogMapper.xml | 2 ++ bms-back/sql/ar/ar_bill.sql | 4 ++-- bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql | 6 ++++++ bms-front/src/views/ar/bill/log.vue | 6 +++--- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml index 2fae1bc2..437bd85a 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml @@ -33,6 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 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, l.from_status, l.to_status, l.before_data, l.after_data, l.operator_id, l.operator_name, @@ -42,6 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 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 and l.ar_bill_id = #{arBillId} and l.object_type = #{objectType} diff --git a/bms-back/sql/ar/ar_bill.sql b/bms-back/sql/ar/ar_bill.sql index fc963a33..d8918318 100644 --- a/bms-back/sql/ar/ar_bill.sql +++ b/bms-back/sql/ar/ar_bill.sql @@ -167,9 +167,9 @@ CREATE TABLE IF NOT EXISTS ar_bill_export ( CREATE TABLE IF NOT EXISTS ar_bill_log ( log_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID', ar_bill_id BIGINT(20) DEFAULT NULL COMMENT '账单ID', - object_type VARCHAR(40) NOT NULL COMMENT 'BILL/RECEIPT/WRITEOFF/EXPORT', + object_type VARCHAR(40) NOT NULL COMMENT 'BILL/RECEIPT/WRITEOFF/EXPORT/BALANCE_WRITEOFF', object_id VARCHAR(64) NOT NULL COMMENT '通用对象ID', - operation_type VARCHAR(40) NOT NULL COMMENT '操作类型', + operation_type VARCHAR(40) NOT NULL COMMENT '操作类型:CONFIRM/VOID/EXPORT/RECEIPT/CANCEL_RECEIPT/WRITEOFF/CANCEL_WRITEOFF/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF', operation_desc VARCHAR(500) DEFAULT NULL COMMENT '操作说明', from_status VARCHAR(30) DEFAULT NULL COMMENT '变更前状态', to_status VARCHAR(30) DEFAULT NULL COMMENT '变更后状态', diff --git a/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql b/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql new file mode 100644 index 00000000..0a11162d --- /dev/null +++ b/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql @@ -0,0 +1,6 @@ +-- 应收日志兼容余额核销:仅更新字段注释,不改变字段类型和历史数据 +ALTER TABLE ar_bill_log + MODIFY COLUMN object_type VARCHAR(40) NOT NULL + COMMENT 'BILL/RECEIPT/WRITEOFF/EXPORT/BALANCE_WRITEOFF', + MODIFY COLUMN operation_type VARCHAR(40) NOT NULL + COMMENT '操作类型:CONFIRM/VOID/EXPORT/RECEIPT/CANCEL_RECEIPT/WRITEOFF/CANCEL_WRITEOFF/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF'; diff --git a/bms-front/src/views/ar/bill/log.vue b/bms-front/src/views/ar/bill/log.vue index de1162f8..f2319083 100644 --- a/bms-front/src/views/ar/bill/log.vue +++ b/bms-front/src/views/ar/bill/log.vue @@ -10,7 +10,7 @@ import { listArBillLogs } from '@/api/ar/bill' const tableRef = ref() const searchColumns = [ - { label: '对象类型', prop: 'objectType', component: 'select', options: [{ label: '应收账单', value: 'BILL' }, { label: '账单导出', value: 'EXPORT' }, { label: '应收收款', value: 'RECEIPT' }, { label: '收款核销', value: 'WRITEOFF' }], width: '130px' }, + { label: '对象类型', prop: 'objectType', component: 'select', options: [{ label: '应收账单', value: 'BILL' }, { label: '账单导出', value: 'EXPORT' }, { label: '应收收款', value: 'RECEIPT' }, { label: '收款核销', value: 'WRITEOFF' }, { label: '余额核销', value: 'BALANCE_WRITEOFF' }], width: '130px' }, { label: '操作类型', prop: 'operationType', component: 'input', width: '140px' }, { label: '结果', prop: 'result', component: 'select', options: [{ label: '成功', value: 'SUCCESS' }, { label: '失败', value: 'FAILED' }], width: '120px' } ] const columns = [ @@ -20,8 +20,8 @@ const columns = [ { label: '结果', prop: 'result', width: 90, slot: 'result' }, { label: '时间', prop: 'createTime', width: 170, parseTime: true } ] -const objectTypeLabels = { BILL: '应收账单', EXPORT: '账单导出', RECEIPT: '应收收款', WRITEOFF: '收款核销' } -const operationLabels = { CONFIRM: '确认账单', VOID: '作废账单', EXPORT: '导出账单', RECEIPT: '登记收款', CANCEL_RECEIPT: '取消收款', WRITEOFF: '收款核销', CANCEL_WRITEOFF: '取消核销' } +const objectTypeLabels = { BILL: '应收账单', EXPORT: '账单导出', RECEIPT: '应收收款', WRITEOFF: '收款核销', BALANCE_WRITEOFF: '余额核销' } +const operationLabels = { CONFIRM: '确认账单', VOID: '作废账单', EXPORT: '导出账单', RECEIPT: '登记收款', CANCEL_RECEIPT: '取消收款', WRITEOFF: '收款核销', CANCEL_WRITEOFF: '取消核销', BALANCE_WRITEOFF: '余额核销', CANCEL_BALANCE_WRITEOFF: '取消余额核销' } const statusLabels = { DRAFT: '草稿', CONFIRMED: '已确认', PARTIAL_WRITTEN_OFF: '部分核销', WRITTEN_OFF: '已核销', VOID: '已作废', REGISTERED: '已登记', CANCELLED: '已取消', EFFECTIVE: '有效' } function objectTypeText(value) { return objectTypeLabels[value] || value || '-' } function operationText(value) { return operationLabels[value] || value || '-' } From e52f52cb35a692dd912e9cf1e0eaa8801484825b Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Wed, 16 Sep 2026 17:57:34 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=BA=94=E6=94=B6?= =?UTF-8?q?=E7=99=BB=E8=AE=B0=E6=94=B6=E6=AC=BE=E5=8A=9F=E8=83=BD=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ar/ArReceiptController.java | 106 ------- .../ruoyi/ar/domain/ArBalanceWriteoff.java | 4 +- .../java/com/ruoyi/ar/domain/ArReceipt.java | 33 --- .../ruoyi/ar/domain/ArReceiptWriteoff.java | 35 --- .../ruoyi/ar/domain/vo/ArReceiptQuery.java | 16 - .../domain/vo/ArReceiptWriteoffRequest.java | 35 --- .../com/ruoyi/ar/enums/ArReceiptStatus.java | 22 -- .../com/ruoyi/ar/enums/ArWriteoffStatus.java | 20 -- .../com/ruoyi/ar/mapper/ArReceiptMapper.java | 30 -- .../ar/mapper/ArReceiptWriteoffMapper.java | 26 -- .../ar/service/IArBalanceWriteoffService.java | 3 +- .../ruoyi/ar/service/IArReceiptService.java | 22 -- .../ar/service/impl/ArReceiptServiceImpl.java | 275 ------------------ .../resources/mapper/ar/ArBillLogMapper.xml | 4 - .../resources/mapper/ar/ArReceiptMapper.xml | 97 ------ .../mapper/ar/ArReceiptWriteoffMapper.xml | 81 ------ bms-back/sql/ar/ar_balance_writeoff.sql | 4 +- bms-back/sql/ar/ar_bill.sql | 49 +--- .../ar_bill_log_balance_writeoff_comment.sql | 4 +- bms-back/sql/ar/ar_menu.sql | 20 +- bms-back/sql/ar/ar_receipt_module_remove.sql | 16 + bms-front/src/views/ar/bill/log.vue | 6 +- 22 files changed, 30 insertions(+), 878 deletions(-) delete mode 100644 bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/ar/ArReceiptController.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceipt.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceiptWriteoff.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptQuery.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptWriteoffRequest.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArReceiptStatus.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArWriteoffStatus.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptMapper.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptWriteoffMapper.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArReceiptService.java delete mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArReceiptServiceImpl.java delete mode 100644 bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptMapper.xml delete mode 100644 bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptWriteoffMapper.xml create mode 100644 bms-back/sql/ar/ar_receipt_module_remove.sql diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/ar/ArReceiptController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/ar/ArReceiptController.java deleted file mode 100644 index 2d8a94a9..00000000 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/ar/ArReceiptController.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.ruoyi.web.controller.ar; - -import java.util.List; -import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import com.ruoyi.ar.domain.ArReceipt; -import com.ruoyi.ar.domain.ArReceiptWriteoff; -import com.ruoyi.ar.domain.vo.ArBillActionRequest; -import com.ruoyi.ar.domain.vo.ArReceiptQuery; -import com.ruoyi.ar.domain.vo.ArReceiptWriteoffRequest; -import com.ruoyi.ar.service.IArReceiptService; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.uuid.IdUtils; - -/** 应收收款与核销管理。 */ -@RestController -@RequestMapping("/ar/receipt") -@Validated -public class ArReceiptController extends BaseController -{ - @Autowired - private IArReceiptService receiptService; - - @PreAuthorize("@ss.hasPermi('ar:receipt:list')") - @GetMapping("/list") - public TableDataInfo list(ArReceiptQuery query) - { - startPage(); - return getDataTable(receiptService.selectReceiptList(query)); - } - - @PreAuthorize("@ss.hasPermi('ar:receipt:list')") - @GetMapping("/{receiptId}/writeoffs") - public AjaxResult writeoffs(@PathVariable Long receiptId) - { - return success(receiptService.selectWriteoffs(receiptId)); - } - - @Log(title = "应收收款", businessType = BusinessType.INSERT) - @PreAuthorize("@ss.hasPermi('ar:receipt:add')") - @PostMapping - public AjaxResult add(@Valid @RequestBody ArReceipt receipt, - @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) - { - String requestId = requestId(headerRequestId); - return success(receiptService.registerReceipt(receipt, getUsername(), requestId)).put("request_id", requestId); - } - - @Log(title = "应收收款", businessType = BusinessType.UPDATE) - @PreAuthorize("@ss.hasPermi('ar:receipt:cancel')") - @PutMapping("/{receiptId}/cancel") - public AjaxResult cancelReceipt(@PathVariable Long receiptId, @RequestBody(required = false) ArBillActionRequest request, - @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) - { - String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId); - receiptService.cancelReceipt(receiptId, request == null ? null : request.getRemark(), getUsername(), requestId); - return success().put("request_id", requestId); - } - - @Log(title = "应收收款核销", businessType = BusinessType.UPDATE) - @PreAuthorize("@ss.hasPermi('ar:receipt:writeoff')") - @PostMapping("/writeoff") - public AjaxResult writeoff(@Valid @RequestBody ArReceiptWriteoffRequest request, - @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) - { - String requestId = StringUtils.isNotEmpty(request.getRequestId()) ? request.getRequestId() : requestId(headerRequestId); - List rows = receiptService.writeoff(request, getUsername(), requestId); - return success(rows).put("request_id", requestId); - } - - @Log(title = "应收收款核销", businessType = BusinessType.UPDATE) - @PreAuthorize("@ss.hasPermi('ar:receipt:cancel')") - @PutMapping("/writeoff/{writeoffId}/cancel") - public AjaxResult cancel(@PathVariable Long writeoffId, @RequestBody(required = false) ArBillActionRequest request, - @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) - { - String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId); - receiptService.cancelWriteoff(writeoffId, request == null ? null : request.getRemark(), getUsername(), requestId); - return success().put("request_id", requestId); - } - - private String requestId(String headerRequestId) - { - return StringUtils.isNotEmpty(headerRequestId) ? headerRequestId : IdUtils.fastSimpleUUID(); - } - - private String requestId(String bodyRequestId, String headerRequestId) - { - return StringUtils.isNotEmpty(bodyRequestId) ? bodyRequestId : requestId(headerRequestId); - } -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArBalanceWriteoff.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArBalanceWriteoff.java index 6a282884..79a34831 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArBalanceWriteoff.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArBalanceWriteoff.java @@ -10,8 +10,8 @@ import lombok.EqualsAndHashCode; /** * 应收账单余额核销 ar_balance_writeoff * - *

用客户账户余额核销应收账单,与 ar_receipt_writeoff(收款核销)并列, - * 两者共享 ar_bill.written_off_amount / unwritten_amount 缓存。

+ *

用客户账户余额核销应收账单,和客户账户余额流水联动, + * 共享 ar_bill.written_off_amount / unwritten_amount 缓存。

* * @author ruoyi * @date 2026-09-16 diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceipt.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceipt.java deleted file mode 100644 index 260f90ac..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceipt.java +++ /dev/null @@ -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; -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceiptWriteoff.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceiptWriteoff.java deleted file mode 100644 index 378a2ce4..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/ArReceiptWriteoff.java +++ /dev/null @@ -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; -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptQuery.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptQuery.java deleted file mode 100644 index f108dac5..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptQuery.java +++ /dev/null @@ -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; -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptWriteoffRequest.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptWriteoffRequest.java deleted file mode 100644 index dfa6239a..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/domain/vo/ArReceiptWriteoffRequest.java +++ /dev/null @@ -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 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; - } -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArReceiptStatus.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArReceiptStatus.java deleted file mode 100644 index 026b6fb4..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArReceiptStatus.java +++ /dev/null @@ -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; - } -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArWriteoffStatus.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArWriteoffStatus.java deleted file mode 100644 index 951c399b..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/enums/ArWriteoffStatus.java +++ /dev/null @@ -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; - } -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptMapper.java deleted file mode 100644 index ac4e3b6a..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptMapper.java +++ /dev/null @@ -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 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); -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptWriteoffMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptWriteoffMapper.java deleted file mode 100644 index 21a1ece1..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/mapper/ArReceiptWriteoffMapper.java +++ /dev/null @@ -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 selectByBillId(Long arBillId); - - List 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); -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArBalanceWriteoffService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArBalanceWriteoffService.java index d39006cc..f363bf1b 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArBalanceWriteoffService.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArBalanceWriteoffService.java @@ -7,8 +7,7 @@ import com.ruoyi.ar.domain.ArBill; /** * 应收账单余额核销 Service接口 * - *

资金来源为客户账户余额(customer_account.balance),与收款核销 - * (ar_receipt_writeoff)并列;核销时三方联动:账户余额扣减、已用额度释放、 + *

资金来源为客户账户余额(customer_account.balance);核销时三方联动:账户余额扣减、已用额度释放、 * 账单已核销/未核销金额及状态更新。

* * @author ruoyi diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArReceiptService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArReceiptService.java deleted file mode 100644 index 3fe46f7e..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/IArReceiptService.java +++ /dev/null @@ -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 selectReceiptList(ArReceiptQuery query); - - List selectWriteoffs(Long receiptId); - - ArReceipt registerReceipt(ArReceipt receipt, String operator, String requestId); - - void cancelReceipt(Long receiptId, String remark, String operator, String requestId); - - List writeoff(ArReceiptWriteoffRequest request, String operator, String requestId); - - void cancelWriteoff(Long writeoffId, String remark, String operator, String requestId); -} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArReceiptServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArReceiptServiceImpl.java deleted file mode 100644 index 2765311c..00000000 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArReceiptServiceImpl.java +++ /dev/null @@ -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 selectReceiptList(ArReceiptQuery query) - { - return receiptMapper.selectArReceiptList(query); - } - - @Override - public List 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 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 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; } } -} diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml index 437bd85a..a1146269 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArBillLogMapper.xml @@ -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 and l.ar_bill_id = #{arBillId} diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptMapper.xml deleted file mode 100644 index 2e50acaa..00000000 --- a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptMapper.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 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' - - - - - 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 - - - - - - - - - - - - - - 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() - ) - - - - update ar_receipt - set unallocated_amount = #{unallocatedAmount}, status = #{status}, - update_by = #{operator}, update_time = sysdate() - where receipt_id = #{receiptId} - - - - update ar_receipt set status = 'CANCELLED', remark = #{remark}, - update_by = #{operator}, update_time = sysdate() - where receipt_id = #{receiptId} - - diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptWriteoffMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptWriteoffMapper.xml deleted file mode 100644 index fa730cdb..00000000 --- a/bms-back/ruoyi-system/src/main/resources/mapper/ar/ArReceiptWriteoffMapper.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - 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 - - - - - 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 - - - - - - - - - - - - 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() - ) - - - - update ar_receipt_writeoff - set status = 'CANCELLED', remark = #{remark}, cancel_by = #{cancelBy}, cancel_time = sysdate() - where writeoff_id = #{writeoffId} and status = 'EFFECTIVE' - - - - - - diff --git a/bms-back/sql/ar/ar_balance_writeoff.sql b/bms-back/sql/ar/ar_balance_writeoff.sql index dc598488..d66d7378 100644 --- a/bms-back/sql/ar/ar_balance_writeoff.sql +++ b/bms-back/sql/ar/ar_balance_writeoff.sql @@ -5,8 +5,8 @@ -- 3) 菜单与授权:客户账单(2435) / 账单详情(2436) / 余额核销(2437) / 反核销(2438) -- ===================================================================== --- 余额核销单:用客户账户余额核销应收账单(与 ar_receipt_writeoff 收款核销并列, --- 两者共享 ar_bill.written_off_amount / unwritten_amount 缓存字段) +-- 余额核销单:用客户账户余额核销应收账单, +-- 与客户账户余额流水配合,共享 ar_bill.written_off_amount / unwritten_amount 缓存字段 CREATE TABLE IF NOT EXISTS ar_balance_writeoff ( writeoff_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '余额核销ID', writeoff_no VARCHAR(50) NOT NULL COMMENT '核销单号(BWO开头)', diff --git a/bms-back/sql/ar/ar_bill.sql b/bms-back/sql/ar/ar_bill.sql index d8918318..a79dd7ad 100644 --- a/bms-back/sql/ar/ar_bill.sql +++ b/bms-back/sql/ar/ar_bill.sql @@ -98,51 +98,6 @@ CREATE TABLE IF NOT EXISTS ar_bill_detail_fee ( CONSTRAINT fk_ar_detail_fee_detail FOREIGN KEY (detail_id) REFERENCES ar_bill_detail (detail_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收账单费用快照'; -CREATE TABLE IF NOT EXISTS ar_receipt ( - receipt_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '收款ID', - receipt_no VARCHAR(50) NOT NULL COMMENT '收款单号', - customer_id BIGINT(20) NOT NULL COMMENT '付款客户ID', - receipt_date DATE NOT NULL COMMENT '到账日期', - currency VARCHAR(10) NOT NULL DEFAULT 'CNY' COMMENT '币种', - receipt_amount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '到账总额', - unallocated_amount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '未分配金额缓存', - payment_method VARCHAR(30) NOT NULL COMMENT '到账方式', - bank_reference VARCHAR(100) DEFAULT NULL COMMENT '银行流水号或第三方交易号', - status VARCHAR(30) NOT NULL COMMENT 'REGISTERED/PARTIAL_WRITTEN_OFF/WRITTEN_OFF/CANCELLED', - remark VARCHAR(500) DEFAULT NULL COMMENT '收款备注或取消原因', - create_by VARCHAR(64) NOT NULL COMMENT '创建人', - create_time DATETIME NOT NULL COMMENT '创建时间', - update_by VARCHAR(64) NOT NULL COMMENT '更新人', - update_time DATETIME NOT NULL COMMENT '更新时间', - PRIMARY KEY (receipt_id), - UNIQUE KEY uk_ar_receipt_no (receipt_no), - UNIQUE KEY uk_ar_payment_reference (payment_method, bank_reference), - KEY idx_ar_receipt_customer_date (customer_id, receipt_date), - KEY idx_ar_receipt_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收收款表'; - -CREATE TABLE IF NOT EXISTS ar_receipt_writeoff ( - writeoff_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '核销ID', - receipt_id BIGINT(20) NOT NULL COMMENT '收款ID', - ar_bill_id BIGINT(20) NOT NULL COMMENT '应收账单ID', - writeoff_no VARCHAR(50) NOT NULL COMMENT '核销单号', - writeoff_amount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '本次核销金额', - currency VARCHAR(10) NOT NULL COMMENT '币种', - writeoff_date DATE NOT NULL COMMENT '核销日期', - status VARCHAR(30) NOT NULL COMMENT 'EFFECTIVE/CANCELLED', - remark VARCHAR(500) DEFAULT NULL COMMENT '核销备注或取消原因', - create_by VARCHAR(64) NOT NULL COMMENT '创建人', - create_time DATETIME NOT NULL COMMENT '创建时间', - cancel_by VARCHAR(64) DEFAULT NULL COMMENT '取消人', - cancel_time DATETIME DEFAULT NULL COMMENT '取消时间', - PRIMARY KEY (writeoff_id), - UNIQUE KEY uk_ar_writeoff_no (writeoff_no), - KEY idx_ar_writeoff_receipt (receipt_id, status), - KEY idx_ar_writeoff_bill (ar_bill_id, status), - CONSTRAINT fk_ar_writeoff_receipt FOREIGN KEY (receipt_id) REFERENCES ar_receipt (receipt_id), - CONSTRAINT fk_ar_writeoff_bill FOREIGN KEY (ar_bill_id) REFERENCES ar_bill (ar_bill_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收收款核销表'; - CREATE TABLE IF NOT EXISTS ar_bill_export ( export_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '导出ID', ar_bill_id BIGINT(20) NOT NULL COMMENT '应收账单ID', @@ -167,9 +122,9 @@ CREATE TABLE IF NOT EXISTS ar_bill_export ( CREATE TABLE IF NOT EXISTS ar_bill_log ( log_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID', ar_bill_id BIGINT(20) DEFAULT NULL COMMENT '账单ID', - object_type VARCHAR(40) NOT NULL COMMENT 'BILL/RECEIPT/WRITEOFF/EXPORT/BALANCE_WRITEOFF', + object_type VARCHAR(40) NOT NULL COMMENT 'BILL/EXPORT/BALANCE_WRITEOFF', object_id VARCHAR(64) NOT NULL COMMENT '通用对象ID', - operation_type VARCHAR(40) NOT NULL COMMENT '操作类型:CONFIRM/VOID/EXPORT/RECEIPT/CANCEL_RECEIPT/WRITEOFF/CANCEL_WRITEOFF/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF', + operation_type VARCHAR(40) NOT NULL COMMENT '操作类型:CONFIRM/VOID/EXPORT/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF', operation_desc VARCHAR(500) DEFAULT NULL COMMENT '操作说明', from_status VARCHAR(30) DEFAULT NULL COMMENT '变更前状态', to_status VARCHAR(30) DEFAULT NULL COMMENT '变更后状态', diff --git a/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql b/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql index 0a11162d..1461df30 100644 --- a/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql +++ b/bms-back/sql/ar/ar_bill_log_balance_writeoff_comment.sql @@ -1,6 +1,6 @@ -- 应收日志兼容余额核销:仅更新字段注释,不改变字段类型和历史数据 ALTER TABLE ar_bill_log MODIFY COLUMN object_type VARCHAR(40) NOT NULL - COMMENT 'BILL/RECEIPT/WRITEOFF/EXPORT/BALANCE_WRITEOFF', + COMMENT 'BILL/EXPORT/BALANCE_WRITEOFF', MODIFY COLUMN operation_type VARCHAR(40) NOT NULL - COMMENT '操作类型:CONFIRM/VOID/EXPORT/RECEIPT/CANCEL_RECEIPT/WRITEOFF/CANCEL_WRITEOFF/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF'; + COMMENT '操作类型:CONFIRM/VOID/EXPORT/BALANCE_WRITEOFF/CANCEL_BALANCE_WRITEOFF'; diff --git a/bms-back/sql/ar/ar_menu.sql b/bms-back/sql/ar/ar_menu.sql index c5b00448..9d75e51f 100644 --- a/bms-back/sql/ar/ar_menu.sql +++ b/bms-back/sql/ar/ar_menu.sql @@ -2,7 +2,7 @@ SET NAMES utf8mb4; INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) -SELECT 2400, N'应收管理', 0, 10, 'ar', NULL, '', '', 1, 0, 'M', '0', '0', '', 'money', 'admin', sysdate(), N'应收账单、收款与核销' +SELECT 2400, N'应收管理', 0, 10, 'ar', NULL, '', '', 1, 0, 'M', '0', '0', '', 'money', 'admin', sysdate(), N'应收账单、余额核销与日志' FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2400); INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) @@ -24,22 +24,6 @@ INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, SELECT 2406, N'导出账单', 2401, 5, '', NULL, 1, 0, 'F', '0', '0', 'ar:bill:export', '#', 'admin', sysdate() FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2406); -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) -SELECT 2410, N'应收收款', 2400, 2, 'receipt', 'ar/receipt/index', 'ArReceipt', 1, 0, 'C', '0', '0', 'ar:receipt:list', 'money', 'admin', sysdate(), N'收款登记与核销' -FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2410); -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) -SELECT 2411, N'收款登记', 2410, 1, '', NULL, 1, 0, 'F', '0', '0', 'ar:receipt:add', '#', 'admin', sysdate() -FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2411); -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) -SELECT 2412, N'批量核销', 2410, 2, '', NULL, 1, 0, 'F', '0', '0', 'ar:receipt:writeoff', '#', 'admin', sysdate() -FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2412); -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) -SELECT 2413, N'取消核销', 2410, 3, '', NULL, 1, 0, 'F', '0', '0', 'ar:receipt:cancel', '#', 'admin', sysdate() -FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2413); -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) -SELECT 2414, N'取消收款', 2410, 4, '', NULL, 1, 0, 'F', '0', '0', 'ar:receipt:cancel', '#', 'admin', sysdate() -FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2414); - INSERT INTO sys_menu ( menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, @@ -53,5 +37,5 @@ FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2420); INSERT INTO sys_role_menu (role_id, menu_id) -SELECT 1, m.menu_id FROM (SELECT 2400 AS menu_id UNION SELECT 2401 UNION SELECT 2402 UNION SELECT 2403 UNION SELECT 2404 UNION SELECT 2405 UNION SELECT 2406 UNION SELECT 2410 UNION SELECT 2411 UNION SELECT 2412 UNION SELECT 2413 UNION SELECT 2414 UNION SELECT 2420) m +SELECT 1, m.menu_id FROM (SELECT 2400 AS menu_id UNION SELECT 2401 UNION SELECT 2402 UNION SELECT 2403 UNION SELECT 2404 UNION SELECT 2405 UNION SELECT 2406 UNION SELECT 2420) m WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id); diff --git a/bms-back/sql/ar/ar_receipt_module_remove.sql b/bms-back/sql/ar/ar_receipt_module_remove.sql new file mode 100644 index 00000000..d0a93a9b --- /dev/null +++ b/bms-back/sql/ar/ar_receipt_module_remove.sql @@ -0,0 +1,16 @@ +-- 应收收款独立模块下线 +-- 业务闭环改为:客户收款登记/审核 -> 客户余额 -> 余额核销账单。 +-- 本脚本会永久删除 ar_receipt、ar_receipt_writeoff 及其历史数据,执行前请确认已完成备份。 + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- 先删除子表,再删除收款主表 +DROP TABLE IF EXISTS ar_receipt_writeoff; +DROP TABLE IF EXISTS ar_receipt; + +-- 删除应收管理下独立“应收收款”菜单及按钮权限 +DELETE FROM sys_role_menu WHERE menu_id IN (2410, 2411, 2412, 2413, 2414); +DELETE FROM sys_menu WHERE menu_id IN (2410, 2411, 2412, 2413, 2414); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/bms-front/src/views/ar/bill/log.vue b/bms-front/src/views/ar/bill/log.vue index f2319083..bb77b4a8 100644 --- a/bms-front/src/views/ar/bill/log.vue +++ b/bms-front/src/views/ar/bill/log.vue @@ -10,7 +10,7 @@ import { listArBillLogs } from '@/api/ar/bill' const tableRef = ref() const searchColumns = [ - { label: '对象类型', prop: 'objectType', component: 'select', options: [{ label: '应收账单', value: 'BILL' }, { label: '账单导出', value: 'EXPORT' }, { label: '应收收款', value: 'RECEIPT' }, { label: '收款核销', value: 'WRITEOFF' }, { label: '余额核销', value: 'BALANCE_WRITEOFF' }], width: '130px' }, + { label: '对象类型', prop: 'objectType', component: 'select', options: [{ label: '应收账单', value: 'BILL' }, { label: '账单导出', value: 'EXPORT' }, { label: '余额核销', value: 'BALANCE_WRITEOFF' }], width: '130px' }, { label: '操作类型', prop: 'operationType', component: 'input', width: '140px' }, { label: '结果', prop: 'result', component: 'select', options: [{ label: '成功', value: 'SUCCESS' }, { label: '失败', value: 'FAILED' }], width: '120px' } ] const columns = [ @@ -20,8 +20,8 @@ const columns = [ { label: '结果', prop: 'result', width: 90, slot: 'result' }, { label: '时间', prop: 'createTime', width: 170, parseTime: true } ] -const objectTypeLabels = { BILL: '应收账单', EXPORT: '账单导出', RECEIPT: '应收收款', WRITEOFF: '收款核销', BALANCE_WRITEOFF: '余额核销' } -const operationLabels = { CONFIRM: '确认账单', VOID: '作废账单', EXPORT: '导出账单', RECEIPT: '登记收款', CANCEL_RECEIPT: '取消收款', WRITEOFF: '收款核销', CANCEL_WRITEOFF: '取消核销', BALANCE_WRITEOFF: '余额核销', CANCEL_BALANCE_WRITEOFF: '取消余额核销' } +const objectTypeLabels = { BILL: '应收账单', EXPORT: '账单导出', BALANCE_WRITEOFF: '余额核销' } +const operationLabels = { CONFIRM: '确认账单', VOID: '作废账单', EXPORT: '导出账单', BALANCE_WRITEOFF: '余额核销', CANCEL_BALANCE_WRITEOFF: '取消余额核销' } const statusLabels = { DRAFT: '草稿', CONFIRMED: '已确认', PARTIAL_WRITTEN_OFF: '部分核销', WRITTEN_OFF: '已核销', VOID: '已作废', REGISTERED: '已登记', CANCELLED: '已取消', EFFECTIVE: '有效' } function objectTypeText(value) { return objectTypeLabels[value] || value || '-' } function operationText(value) { return operationLabels[value] || value || '-' } From 6487146117d61aafb59ec2fe575633b74fdb8ff1 Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Wed, 16 Sep 2026 17:57:48 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=BA=94=E6=94=B6?= =?UTF-8?q?=E7=99=BB=E8=AE=B0=E6=94=B6=E6=AC=BE=E5=8A=9F=E8=83=BD=E9=80=BB?= =?UTF-8?q?=E8=BE=912?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bms-front/src/api/ar/receipt.js | 25 ------ bms-front/src/views/ar/receipt/index.vue | 105 ----------------------- 2 files changed, 130 deletions(-) delete mode 100644 bms-front/src/api/ar/receipt.js delete mode 100644 bms-front/src/views/ar/receipt/index.vue diff --git a/bms-front/src/api/ar/receipt.js b/bms-front/src/api/ar/receipt.js deleted file mode 100644 index 1fc6357d..00000000 --- a/bms-front/src/api/ar/receipt.js +++ /dev/null @@ -1,25 +0,0 @@ -import request from '@/utils/request' - -export function listArReceipt(query) { - return request({ url: '/ar/receipt/list', method: 'get', params: query }) -} - -export function addArReceipt(data) { - return request({ url: '/ar/receipt', method: 'post', data }) -} - -export function cancelArReceipt(receiptId, data = {}) { - return request({ url: '/ar/receipt/' + receiptId + '/cancel', method: 'put', data }) -} - -export function listArWriteoffs(receiptId) { - return request({ url: '/ar/receipt/' + receiptId + '/writeoffs', method: 'get' }) -} - -export function writeoffArReceipt(data) { - return request({ url: '/ar/receipt/writeoff', method: 'post', data }) -} - -export function cancelArWriteoff(writeoffId, data = {}) { - return request({ url: '/ar/receipt/writeoff/' + writeoffId + '/cancel', method: 'put', data }) -} diff --git a/bms-front/src/views/ar/receipt/index.vue b/bms-front/src/views/ar/receipt/index.vue deleted file mode 100644 index 96f49ec0..00000000 --- a/bms-front/src/views/ar/receipt/index.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - - - From 324da1baa18f1ae2f74384a0e674f5c8e847d279 Mon Sep 17 00:00:00 2001 From: luojiaquan <1583622118@qq.com> Date: Thu, 17 Sep 2026 09:00:26 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E9=A2=9D=E5=BA=A6=E5=AE=A1=E6=89=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customermoney/CustomerBillController.java | 28 +- .../CustomerMoneyController.java | 76 +++- .../domain/CustomerCreditApply.java | 102 ++++++ .../mapper/CustomerCreditApplyMapper.java | 35 ++ .../service/ICustomerMoneyService.java | 26 +- .../impl/CustomerMoneyServiceImpl.java | 341 ++++++++++++++---- .../customer/CustomerCreditApplyMapper.xml | 129 +++++++ .../customer_credit_apply.sql | 72 ++++ .../customermoney/customerbill.js | 16 - .../customermoney/customermoney.js | 42 ++- bms-front/src/views/ar/bill/index.vue | 7 +- .../customerinfo/customerbill/index.vue | 301 ---------------- .../customerinfo/customermoney/index.vue | 213 ++++++++++- .../customerinfo/index.vue | 25 +- 14 files changed, 974 insertions(+), 439 deletions(-) create mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/domain/CustomerCreditApply.java create mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/mapper/CustomerCreditApplyMapper.java create mode 100644 bms-back/ruoyi-system/src/main/resources/mapper/customer/CustomerCreditApplyMapper.xml create mode 100644 bms-back/sql/customer+supplier/customer_credit_apply.sql delete mode 100644 bms-front/src/views/customersuppliermanagement/customerinfo/customerbill/index.vue diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerBillController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerBillController.java index 708c440a..843d41f5 100644 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerBillController.java +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerBillController.java @@ -13,8 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.ar.domain.ArBalanceWriteoff; import com.ruoyi.ar.service.IArBalanceWriteoffService; -import com.ruoyi.ar.service.IArBillService; -import com.ruoyi.ar.domain.vo.ArBillQuery; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -22,7 +20,7 @@ import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; /** - * 客户余额核销应收账单 / 账单只读查询 Controller + * 客户余额核销应收账单 Controller(账单查看统一走应收管理-应收账单页面) * * @author ruoyi * @date 2026-09-16 @@ -34,9 +32,6 @@ public class CustomerBillController extends BaseController @Autowired private IArBalanceWriteoffService balanceWriteoffService; - @Autowired - private IArBillService billService; - /** * 查询客户可核销账单(已确认/部分核销,未核销额大于0) */ @@ -87,25 +82,4 @@ public class CustomerBillController extends BaseController balanceWriteoffService.cancelWriteoff(writeoffId, body == null ? null : body.getCancelRemark()); return success(); } - - /** - * 账单查看:分页查询应收账单(只读,供客户侧账单查看页使用) - */ - @PreAuthorize("@ss.hasPermi('customer:bill:list')") - @GetMapping("/list") - public TableDataInfo billList(ArBillQuery query) - { - startPage(); - return getDataTable(billService.selectArBillList(query)); - } - - /** - * 账单查看:账单详情(含订单明细/费用明细) - */ - @PreAuthorize("@ss.hasPermi('customer:bill:query')") - @GetMapping("/{arBillId}") - public AjaxResult billInfo(@PathVariable("arBillId") Long arBillId) - { - return success(billService.selectArBillById(arBillId)); - } } diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerMoneyController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerMoneyController.java index 26058a3b..e8028dbd 100644 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerMoneyController.java +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/customermoney/CustomerMoneyController.java @@ -17,6 +17,7 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow; +import com.ruoyi.customer.customermoney.domain.CustomerCreditApply; import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow; import com.ruoyi.customer.customermoney.domain.CustomerReceipt; import com.ruoyi.customer.customermoney.service.ICustomerMoneyService; @@ -136,15 +137,76 @@ public class CustomerMoneyController extends BaseController } /** - * 调整客户额度(信用额度/临时额度),调整过程留额度流水 + * 提交额度调整申请(信用额度/临时额度),申请待审批通过后才真正调整额度 */ @PreAuthorize("@ss.hasPermi('customer:credit:adjust')") - @Log(title = "客户额度调整", businessType = BusinessType.UPDATE) - @PostMapping("/credit/adjust") - public AjaxResult adjustCredit(@RequestBody CustomerCreditFlow flow) + @Log(title = "客户额度调整申请", businessType = BusinessType.INSERT) + @PostMapping("/credit/apply") + public AjaxResult applyCredit(@RequestBody CustomerCreditApply apply) { - flow.setCreateBy(getUsername()); - flow.setOperatorId(getUserId()); - return toAjax(customerMoneyService.adjustCredit(flow)); + apply.setCreateBy(getUsername()); + return toAjax(customerMoneyService.applyCreditAdjust(apply)); + } + + /** + * 查询额度调整申请列表 + */ + @PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')") + @GetMapping("/credit/apply/list") + public TableDataInfo creditApplyList(CustomerCreditApply query) + { + if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) { + return getDataTable(Collections.emptyList()); + } + startPage(); + List list = customerMoneyService.selectCreditApplyList(query); + return getDataTable(list); + } + + /** + * 查询额度调整申请详情 + */ + @PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')") + @GetMapping("/credit/apply/{applyId}") + public AjaxResult getCreditApply(@PathVariable("applyId") Long applyId) + { + return success(customerMoneyService.selectCreditApplyById(applyId)); + } + + /** + * 审批通过额度调整申请:通过后调整账户额度并生成额度流水 + */ + @PreAuthorize("@ss.hasPermi('customer:credit:approve')") + @Log(title = "客户额度调整审批", businessType = BusinessType.UPDATE) + @PutMapping("/credit/apply/approve/{applyId}") + public AjaxResult approveCreditApply(@PathVariable("applyId") Long applyId, + @RequestBody(required = false) CustomerCreditApply body) + { + String remark = body != null ? body.getApproveRemark() : null; + return toAjax(customerMoneyService.approveCreditApply(applyId, remark)); + } + + /** + * 驳回额度调整申请(驳回原因必填) + */ + @PreAuthorize("@ss.hasPermi('customer:credit:approve')") + @Log(title = "客户额度调整驳回", businessType = BusinessType.UPDATE) + @PutMapping("/credit/apply/reject/{applyId}") + public AjaxResult rejectCreditApply(@PathVariable("applyId") Long applyId, + @RequestBody CustomerCreditApply body) + { + String reason = body != null ? body.getApproveRemark() : null; + return toAjax(customerMoneyService.rejectCreditApply(applyId, reason)); + } + + /** + * 撤销待审批的额度调整申请 + */ + @PreAuthorize("@ss.hasPermi('customer:credit:adjust')") + @Log(title = "客户额度调整撤销", businessType = BusinessType.UPDATE) + @PutMapping("/credit/apply/cancel/{applyId}") + public AjaxResult cancelCreditApply(@PathVariable("applyId") Long applyId) + { + return toAjax(customerMoneyService.cancelCreditApply(applyId)); } } diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/domain/CustomerCreditApply.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/domain/CustomerCreditApply.java new file mode 100644 index 00000000..0ce23ce7 --- /dev/null +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/domain/CustomerCreditApply.java @@ -0,0 +1,102 @@ +package com.ruoyi.customer.customermoney.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 客户额度调整申请审批 customer_credit_apply + * 信用额度/额外额度(临时额度)调整先申请,审批通过后才真正调整账户额度 + * + * @author ruoyi + * @date 2026-09-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CustomerCreditApply extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 待审批 */ + public static final String STATUS_PENDING = "PENDING"; + /** 已通过 */ + public static final String STATUS_APPROVED = "APPROVED"; + /** 已驳回 */ + public static final String STATUS_REJECTED = "REJECTED"; + /** 已撤销 */ + public static final String STATUS_CANCELLED = "CANCELLED"; + + /** 申请单ID */ + private Long applyId; + + /** 申请单号(EDSQ前缀) */ + private String applyNo; + + /** 客户ID */ + private Long customerId; + + /** 客户账户ID */ + private Long accountId; + + /** 额度类型:CREDIT信用额度 TEMP临时额度(额外额度) */ + private String creditType; + + /** 调整方式:SET设置为 ADD增加 SUB减少 */ + private String adjustMode; + + /** 调整金额(正数) */ + private java.math.BigDecimal changeAmount; + + /** 临时额度生效日期 */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date effectiveDate; + + /** 临时额度失效日期(空=长期) */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date expireDate; + + /** 调整原因 */ + private String reason; + + /** 状态:PENDING/APPROVED/REJECTED/CANCELLED */ + private String status; + + /** 申请人 */ + private String applyBy; + + /** 申请人ID */ + private Long applyUserId; + + /** 申请时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date applyTime; + + /** 审批人 */ + private String approveBy; + + /** 审批人ID */ + private Long approveUserId; + + /** 审批时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date approveTime; + + /** 审批意见/驳回原因 */ + private String approveRemark; + + /** 审批通过后生成的额度流水ID */ + private Long flowId; + + /** 删除标志 */ + private String delFlag; + + /* -------- 展示字段 -------- */ + + /** 客户编码 */ + private String customerCode; + + /** 客户名称 */ + private String customerName; +} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/mapper/CustomerCreditApplyMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/mapper/CustomerCreditApplyMapper.java new file mode 100644 index 00000000..964b91dc --- /dev/null +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/mapper/CustomerCreditApplyMapper.java @@ -0,0 +1,35 @@ +package com.ruoyi.customer.customermoney.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.customer.customermoney.domain.CustomerCreditApply; + +/** + * 客户额度调整申请审批 Mapper + * + * @author ruoyi + * @date 2026-09-16 + */ +public interface CustomerCreditApplyMapper +{ + /** 新增申请 */ + int insertApply(CustomerCreditApply apply); + + /** 按ID查申请(行锁,审批事务使用) */ + CustomerCreditApply selectApplyByIdForUpdate(@Param("applyId") Long applyId); + + /** 按ID查申请 */ + CustomerCreditApply selectApplyById(@Param("applyId") Long applyId); + + /** 查询申请列表(可按客户/状态/类型过滤) */ + List selectApplyList(CustomerCreditApply query); + + /** 审批通过:PENDING -> APPROVED,回写账户ID、流水ID、审批人/时间/意见 */ + int updateApproved(CustomerCreditApply apply); + + /** 审批驳回:PENDING -> REJECTED */ + int updateRejected(CustomerCreditApply apply); + + /** 申请人撤销:PENDING -> CANCELLED */ + int updateCancelled(@Param("applyId") Long applyId, @Param("updateBy") String updateBy); +} diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/ICustomerMoneyService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/ICustomerMoneyService.java index daa468fa..2a955384 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/ICustomerMoneyService.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/ICustomerMoneyService.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.List; import com.ruoyi.customer.customermoney.domain.CustomerAccount; import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow; +import com.ruoyi.customer.customermoney.domain.CustomerCreditApply; import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow; import com.ruoyi.customer.customermoney.domain.CustomerReceipt; @@ -52,11 +53,28 @@ public interface ICustomerMoneyService List selectCreditFlowList(CustomerCreditFlow query); /** - * 调整客户额度(信用额度/临时额度) - * 事务内:锁定CNY账户(不存在则初始化)-> 校验(不能为负、不能低于已用额度) - * -> 乐观锁更新额度 -> 回写客户档案额度字段 -> 写额度流水 + * 提交额度调整申请(信用额度/临时额度)。申请仅落库为 PENDING,不立即变动账户额度; + * 申请时会按账户当前额度做一次合法性校验(不能为负、不能低于已用额度),审批通过时会再次校验。 */ - int adjustCredit(CustomerCreditFlow flow); + int applyCreditAdjust(CustomerCreditApply apply); + + /** + * 审批通过额度调整申请:PENDING -> APPROVED。 + * 事务内重新锁定CNY账户并按审批时的实际额度计算/校验,通过后调整额度、回写客户档案、写额度流水。 + */ + int approveCreditApply(Long applyId, String approveRemark); + + /** 审批驳回:PENDING -> REJECTED,驳回原因必填 */ + int rejectCreditApply(Long applyId, String rejectReason); + + /** 申请人撤销:PENDING -> CANCELLED */ + int cancelCreditApply(Long applyId); + + /** 查询额度调整申请列表 */ + List selectCreditApplyList(CustomerCreditApply query); + + /** 查询额度调整申请详情 */ + CustomerCreditApply selectCreditApplyById(Long applyId); /** * 账单确认时占用授信额度(used_credit += amount)。该币种账户不存在时跳过。 diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java index 69ce261c..ad9bb948 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java @@ -20,8 +20,10 @@ import com.ruoyi.customer.customerinfo.domain.BmsCustomerAttachment; import com.ruoyi.customer.customerinfo.mapper.BmsCustomerMapper; import com.ruoyi.customer.customermoney.domain.CustomerAccount; import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow; +import com.ruoyi.customer.customermoney.domain.CustomerCreditApply; import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow; import com.ruoyi.customer.customermoney.domain.CustomerReceipt; +import com.ruoyi.customer.customermoney.mapper.CustomerCreditApplyMapper; import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper; import com.ruoyi.customer.customermoney.service.ICustomerMoneyService; import com.ruoyi.system.domain.BmsExchangeRate; @@ -48,6 +50,9 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService @Autowired private CustomerMoneyMapper customerMoneyMapper; + @Autowired + private CustomerCreditApplyMapper creditApplyMapper; + @Autowired private BmsCustomerMapper bmsCustomerMapper; @@ -268,53 +273,27 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService return customerMoneyMapper.selectCreditFlowList(query); } - @Override - @Transactional(rollbackFor = Exception.class) - public int adjustCredit(CustomerCreditFlow cmd) + /** + * 执行额度调整(额度申请审批通过后调用)。 + * 锁CNY账户 -> 按审批时实际额度计算并校验 -> 乐观锁更新 -> 回写客户档案 -> 写额度流水。 + * 返回已落库的额度流水(含ID)。 + */ + private CustomerCreditFlow executeAdjustCredit(CustomerCreditFlow cmd) { - if (cmd == null || cmd.getCustomerId() == null) - { - throw new ServiceException("客户ID不能为空"); - } - Long customerId = cmd.getCustomerId(); - String creditType = cmd.getCreditType(); - if (!CustomerCreditFlow.CREDIT_TYPE_CREDIT.equals(creditType) - && !CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType)) - { - throw new ServiceException("额度类型不正确"); - } - String adjustMode = cmd.getAdjustMode(); - if (!CustomerCreditFlow.MODE_SET.equals(adjustMode) - && !CustomerCreditFlow.MODE_ADD.equals(adjustMode) - && !CustomerCreditFlow.MODE_SUB.equals(adjustMode)) - { - throw new ServiceException("调整方式不正确"); - } - BigDecimal input = nz(cmd.getChangeAmount()); - if (input.compareTo(BigDecimal.ZERO) <= 0) - { - throw new ServiceException("调整金额必须大于0"); - } - if (!StringUtils.hasText(cmd.getReason())) - { - throw new ServiceException("请填写调整原因"); - } - String reason = cmd.getReason().trim(); - if (reason.length() > 255) - { - throw new ServiceException("调整原因不能超过255个字"); - } String username = SecurityUtils.getUsername(); Long userId = SecurityUtils.getUserId(); + Long customerId = cmd.getCustomerId(); + String creditType = cmd.getCreditType(); + String adjustMode = cmd.getAdjustMode(); + String reason = cmd.getReason(); boolean isTemp = CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType); - // 1. 行锁锁定CNY账户(不存在则按客户档案额度初始化创建);先处理临时额度过期 - CustomerAccount account = getOrInitCnyAccount(customerId, username); - expireTempCreditIfNeeded(account); + // 1. 参数/日期/结果合法性校验并锁定CNY账户(不存在则按客户档案额度初始化);先处理临时额度过期 + CustomerAccount account = validateAdjustCommand(cmd, username); + BigDecimal input = nz(cmd.getChangeAmount()); // 2. 计算调整结果 BigDecimal before = isTemp ? nz(account.getExtraCredit()) : nz(account.getTotalCredit()); - BigDecimal otherCredit = isTemp ? nz(account.getTotalCredit()) : nz(account.getExtraCredit()); BigDecimal after; switch (adjustMode) { @@ -329,40 +308,18 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService break; } after = after.setScale(MONEY_SCALE, RoundingMode.HALF_UP); - if (after.compareTo(BigDecimal.ZERO) < 0) - { - throw new ServiceException("调整后额度不能为负数"); - } - if (otherCredit.add(after).compareTo(nz(account.getUsedCredit())) < 0) - { - throw new ServiceException("调整后授信总额度不能小于已用额度:" - + nz(account.getUsedCredit()).toPlainString()); - } - if (after.compareTo(before) == 0) - { - throw new ServiceException("额度未发生变化,无需调整"); - } - // 3. 临时额度有效期校验(不允许预约未来生效,失效日必须晚于生效日) + // 3. 临时额度有效期处理(不允许预约未来生效,失效日必须晚于生效日) Date effectiveDate = null; Date expireDate = null; if (isTemp) { LocalDate today = LocalDate.now(); LocalDate eff = cmd.getEffectiveDate() != null ? toLocalDate(cmd.getEffectiveDate()) : today; - if (eff.isAfter(today)) - { - throw new ServiceException("生效日期不能晚于今天,需要未来生效请在生效当天再设置"); - } effectiveDate = toSqlDate(eff); if (cmd.getExpireDate() != null) { - LocalDate exp = toLocalDate(cmd.getExpireDate()); - if (!exp.isAfter(eff)) - { - throw new ServiceException("失效日期必须晚于生效日期"); - } - expireDate = toSqlDate(exp); + expireDate = toSqlDate(toLocalDate(cmd.getExpireDate())); } account.setExtraCredit(after); account.setExtraEffectiveDate(effectiveDate); @@ -408,7 +365,263 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService flow.setRemark(cmd.getRemark()); flow.setDelFlag("0"); customerMoneyMapper.insertCreditFlow(flow); - return 1; + return flow; + } + + /* ================= 额度调整:申请 / 审批 ================= */ + + @Override + public List selectCreditApplyList(CustomerCreditApply query) + { + return creditApplyMapper.selectApplyList(query); + } + + @Override + public CustomerCreditApply selectCreditApplyById(Long applyId) + { + return creditApplyMapper.selectApplyById(applyId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int applyCreditAdjust(CustomerCreditApply apply) + { + if (apply == null || apply.getCustomerId() == null) + { + throw new ServiceException("客户ID不能为空"); + } + // 构造内部调整命令,复用统一的参数/日期/额度合法性校验(只校验,不写入) + CustomerCreditFlow cmd = new CustomerCreditFlow(); + cmd.setCustomerId(apply.getCustomerId()); + cmd.setCreditType(apply.getCreditType()); + cmd.setAdjustMode(apply.getAdjustMode()); + cmd.setChangeAmount(nz(apply.getChangeAmount())); + cmd.setReason(apply.getReason()); + cmd.setRemark(apply.getRemark()); + cmd.setEffectiveDate(apply.getEffectiveDate()); + cmd.setExpireDate(apply.getExpireDate()); + + String username = SecurityUtils.getUsername(); + Long userId = SecurityUtils.getUserId(); + + // 1. 参数校验 + 日期校验 + 按当前账户额度预演结果,提前拦截不可能通过的申请(不写入账户) + validateAdjustCommand(cmd, username); + apply.setChangeAmount(cmd.getChangeAmount()); + apply.setReason(cmd.getReason()); + + // 2. 待审批申请落库,不动账户额度 + apply.setApplyNo(generateNo("EDSQ")); + apply.setStatus(CustomerCreditApply.STATUS_PENDING); + apply.setApplyBy(username); + apply.setApplyUserId(userId); + apply.setApplyTime(new Date()); + apply.setCreateBy(username); + return creditApplyMapper.insertApply(apply); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int approveCreditApply(Long applyId, String approveRemark) + { + if (applyId == null) + { + throw new ServiceException("申请单ID不能为空"); + } + CustomerCreditApply apply = creditApplyMapper.selectApplyByIdForUpdate(applyId); + if (apply == null) + { + throw new ServiceException("额度调整申请不存在"); + } + if (!CustomerCreditApply.STATUS_PENDING.equals(apply.getStatus())) + { + throw new ServiceException("该申请已处理,不能重复审批"); + } + String username = SecurityUtils.getUsername(); + Long userId = SecurityUtils.getUserId(); + + // 以审批时账户的实际额度重新计算并调整(申请期间额度可能已变动) + CustomerCreditFlow cmd = new CustomerCreditFlow(); + cmd.setCustomerId(apply.getCustomerId()); + cmd.setCreditType(apply.getCreditType()); + cmd.setAdjustMode(apply.getAdjustMode()); + cmd.setChangeAmount(nz(apply.getChangeAmount())); + cmd.setReason(apply.getReason()); + cmd.setRemark(apply.getRemark()); + cmd.setEffectiveDate(apply.getEffectiveDate()); + cmd.setExpireDate(apply.getExpireDate()); + CustomerCreditFlow flow = executeAdjustCredit(cmd); + + CustomerCreditApply update = new CustomerCreditApply(); + update.setApplyId(applyId); + update.setAccountId(flow.getAccountId()); + update.setFlowId(flow.getId()); + update.setApproveBy(username); + update.setApproveUserId(userId); + update.setApproveRemark(approveRemark); + int rows = creditApplyMapper.updateApproved(update); + if (rows == 0) + { + throw new ServiceException("申请状态已变化,审批失败"); + } + return rows; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int rejectCreditApply(Long applyId, String rejectReason) + { + if (applyId == null) + { + throw new ServiceException("申请单ID不能为空"); + } + if (!StringUtils.hasText(rejectReason)) + { + throw new ServiceException("请填写驳回原因"); + } + String reason = rejectReason.trim(); + if (reason.length() > 255) + { + throw new ServiceException("驳回原因不能超过255个字"); + } + CustomerCreditApply apply = creditApplyMapper.selectApplyByIdForUpdate(applyId); + if (apply == null) + { + throw new ServiceException("额度调整申请不存在"); + } + if (!CustomerCreditApply.STATUS_PENDING.equals(apply.getStatus())) + { + throw new ServiceException("该申请已处理,不能重复审批"); + } + CustomerCreditApply update = new CustomerCreditApply(); + update.setApplyId(applyId); + update.setApproveBy(SecurityUtils.getUsername()); + update.setApproveUserId(SecurityUtils.getUserId()); + update.setApproveRemark(reason); + int rows = creditApplyMapper.updateRejected(update); + if (rows == 0) + { + throw new ServiceException("申请状态已变化,驳回失败"); + } + return rows; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int cancelCreditApply(Long applyId) + { + if (applyId == null) + { + throw new ServiceException("申请单ID不能为空"); + } + CustomerCreditApply apply = creditApplyMapper.selectApplyByIdForUpdate(applyId); + if (apply == null) + { + throw new ServiceException("额度调整申请不存在"); + } + if (!CustomerCreditApply.STATUS_PENDING.equals(apply.getStatus())) + { + throw new ServiceException("仅待审批的申请可以撤销"); + } + int rows = creditApplyMapper.updateCancelled(applyId, SecurityUtils.getUsername()); + if (rows == 0) + { + throw new ServiceException("申请状态已变化,撤销失败"); + } + return rows; + } + + /** + * 额度调整参数/日期/结果合法性校验(申请与审批共用)。 + * 锁定CNY账户做预演但不写入,返回当前账户;规整后的金额/原因/日期回写到 cmd。 + */ + private CustomerAccount validateAdjustCommand(CustomerCreditFlow cmd, String username) + { + if (cmd == null || cmd.getCustomerId() == null) + { + throw new ServiceException("客户ID不能为空"); + } + String creditType = cmd.getCreditType(); + if (!CustomerCreditFlow.CREDIT_TYPE_CREDIT.equals(creditType) + && !CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType)) + { + throw new ServiceException("额度类型不正确"); + } + String adjustMode = cmd.getAdjustMode(); + if (!CustomerCreditFlow.MODE_SET.equals(adjustMode) + && !CustomerCreditFlow.MODE_ADD.equals(adjustMode) + && !CustomerCreditFlow.MODE_SUB.equals(adjustMode)) + { + throw new ServiceException("调整方式不正确"); + } + BigDecimal input = nz(cmd.getChangeAmount()); + if (input.compareTo(BigDecimal.ZERO) <= 0) + { + throw new ServiceException("调整金额必须大于0"); + } + if (!StringUtils.hasText(cmd.getReason())) + { + throw new ServiceException("请填写调整原因"); + } + String reason = cmd.getReason().trim(); + if (reason.length() > 255) + { + throw new ServiceException("调整原因不能超过255个字"); + } + cmd.setChangeAmount(input); + cmd.setReason(reason); + boolean isTemp = CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType); + + CustomerAccount account = getOrInitCnyAccount(cmd.getCustomerId(), username); + expireTempCreditIfNeeded(account); + + BigDecimal before = isTemp ? nz(account.getExtraCredit()) : nz(account.getTotalCredit()); + BigDecimal otherCredit = isTemp ? nz(account.getTotalCredit()) : nz(account.getExtraCredit()); + BigDecimal after; + switch (adjustMode) + { + case CustomerCreditFlow.MODE_SET: + after = input; + break; + case CustomerCreditFlow.MODE_ADD: + after = before.add(input); + break; + default: + after = before.subtract(input); + break; + } + after = after.setScale(MONEY_SCALE, RoundingMode.HALF_UP); + if (after.compareTo(BigDecimal.ZERO) < 0) + { + throw new ServiceException("调整后额度不能为负数"); + } + if (otherCredit.add(after).compareTo(nz(account.getUsedCredit())) < 0) + { + throw new ServiceException("调整后授信总额度不能小于已用额度:" + + nz(account.getUsedCredit()).toPlainString()); + } + if (after.compareTo(before) == 0) + { + throw new ServiceException("额度未发生变化,无需调整"); + } + + if (isTemp) + { + LocalDate today = LocalDate.now(); + LocalDate eff = cmd.getEffectiveDate() != null ? toLocalDate(cmd.getEffectiveDate()) : today; + if (eff.isAfter(today)) + { + throw new ServiceException("生效日期不能晚于今天,需要未来生效请在生效当天再提交"); + } + if (cmd.getExpireDate() != null) + { + LocalDate exp = toLocalDate(cmd.getExpireDate()); + if (!exp.isAfter(eff)) + { + throw new ServiceException("失效日期必须晚于生效日期"); + } + } + } + return account; } /* ================= 账单联动:额度占用 / 余额核销 ================= */ diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/customer/CustomerCreditApplyMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/customer/CustomerCreditApplyMapper.xml new file mode 100644 index 00000000..aac36186 --- /dev/null +++ b/bms-back/ruoyi-system/src/main/resources/mapper/customer/CustomerCreditApplyMapper.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select a.apply_id, a.apply_no, a.customer_id, a.account_id, a.credit_type, a.adjust_mode, + a.change_amount, a.effective_date, a.expire_date, a.reason, a.status, + a.apply_by, a.apply_user_id, a.apply_time, + a.approve_by, a.approve_user_id, a.approve_time, a.approve_remark, a.flow_id, + a.create_by, a.create_time, a.update_by, a.update_time, a.remark, a.del_flag, + c.customer_code, c.customer_name + from customer_credit_apply a + left join bms_customer c on c.customer_id = a.customer_id + + + + + + + + + + insert into customer_credit_apply ( + apply_no, customer_id, credit_type, adjust_mode, change_amount, + effective_date, expire_date, reason, status, + apply_by, apply_user_id, apply_time, + create_by, create_time, remark + ) values ( + #{applyNo}, #{customerId}, #{creditType}, #{adjustMode}, #{changeAmount}, + #{effectiveDate}, #{expireDate}, #{reason}, #{status}, + #{applyBy}, #{applyUserId}, #{applyTime}, + #{createBy}, sysdate(), #{remark} + ) + + + + update customer_credit_apply + set status = 'APPROVED', + account_id = #{accountId}, + flow_id = #{flowId}, + approve_by = #{approveBy}, + approve_user_id = #{approveUserId}, + approve_time = sysdate(), + approve_remark = #{approveRemark}, + update_by = #{approveBy}, + update_time = sysdate() + where apply_id = #{applyId} and status = 'PENDING' and del_flag = '0' + + + + update customer_credit_apply + set status = 'REJECTED', + approve_by = #{approveBy}, + approve_user_id = #{approveUserId}, + approve_time = sysdate(), + approve_remark = #{approveRemark}, + update_by = #{approveBy}, + update_time = sysdate() + where apply_id = #{applyId} and status = 'PENDING' and del_flag = '0' + + + + update customer_credit_apply + set status = 'CANCELLED', + update_by = #{updateBy}, + update_time = sysdate() + where apply_id = #{applyId} and status = 'PENDING' and del_flag = '0' + + + diff --git a/bms-back/sql/customer+supplier/customer_credit_apply.sql b/bms-back/sql/customer+supplier/customer_credit_apply.sql new file mode 100644 index 00000000..be1c6546 --- /dev/null +++ b/bms-back/sql/customer+supplier/customer_credit_apply.sql @@ -0,0 +1,72 @@ +-- ============================================================= +-- 客户额度调整申请/审批表 +-- 信用额度、额外额度(临时额度)调整不再直接生效: +-- 先申请(PENDING)-> 审批通过(APPROVED)后才真正调整 customer_account +-- 并生成 customer_credit_flow 额度流水;可驳回(REJECTED)/撤销(CANCELLED) +-- 2026-09-16 +-- ============================================================= +SET NAMES utf8mb4; + +CREATE TABLE IF NOT EXISTS `customer_credit_apply` ( + `apply_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '申请单ID', + `apply_no` VARCHAR(64) NOT NULL COMMENT '申请单号(EDSQ前缀)', + `customer_id` BIGINT(20) NOT NULL COMMENT '客户ID', + `account_id` BIGINT(20) DEFAULT NULL COMMENT '客户账户ID(审批通过时回写)', + `credit_type` VARCHAR(16) NOT NULL COMMENT '额度类型:CREDIT信用额度 TEMP临时额度(额外额度)', + `adjust_mode` VARCHAR(8) NOT NULL COMMENT '调整方式:SET设置为 ADD增加 SUB减少', + `change_amount` DECIMAL(18,2) NOT NULL COMMENT '调整金额(恒为正数,方式看adjust_mode)', + `effective_date` DATE DEFAULT NULL COMMENT '临时额度生效日期', + `expire_date` DATE DEFAULT NULL COMMENT '临时额度失效日期(空=长期)', + `reason` VARCHAR(255) NOT NULL COMMENT '调整原因', + `status` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '状态:PENDING待审批 APPROVED已通过 REJECTED已驳回 CANCELLED已撤销', + `apply_by` VARCHAR(64) DEFAULT '' COMMENT '申请人', + `apply_user_id` BIGINT(20) DEFAULT NULL COMMENT '申请人ID', + `apply_time` DATETIME DEFAULT NULL COMMENT '申请时间', + `approve_by` VARCHAR(64) DEFAULT '' COMMENT '审批人', + `approve_user_id` BIGINT(20) DEFAULT NULL COMMENT '审批人ID', + `approve_time` DATETIME DEFAULT NULL COMMENT '审批时间', + `approve_remark` VARCHAR(255) DEFAULT NULL COMMENT '审批意见/驳回原因', + `flow_id` BIGINT(20) DEFAULT NULL COMMENT '审批通过后生成的额度流水ID(customer_credit_flow.id)', + `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注', + `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `del_flag` CHAR(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)', + PRIMARY KEY (`apply_id`), + UNIQUE KEY `uk_apply_no` (`apply_no`), + KEY `idx_customer_status` (`customer_id`, `status`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='客户额度调整申请审批表'; + +-- ============================================================= +-- 菜单:额度审批(挂在 2422 客户余额管理 下,与 2434 额度调整/申请 并列) +-- ============================================================= +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) +SELECT 2439, N'额度审批', 2422, 2, '', NULL, 1, 0, 'F', '0', '0', 'customer:credit:approve', '#', 'admin', sysdate(), '客户信用/额外额度调整申请审批' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2439); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 2, 2439 FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu WHERE role_id = 2 AND menu_id = 2439); + +-- ============================================================= +-- 下线“客户账单”独立页面(需求2): +-- 余额页“查看账单”改为跳转应收管理-应收账单(/ar/bill,菜单2401)并带客户条件 +-- 2435 客户账单(C) / 2436 账单详情(F) 删除; +-- 2437 余额核销 / 2438 取消余额核销 仍在客户余额页使用,改挂到 2422 客户余额管理 +-- ============================================================= +DELETE FROM sys_role_menu WHERE menu_id IN (2435, 2436); +DELETE FROM sys_menu WHERE menu_id IN (2435, 2436); +UPDATE sys_menu SET parent_id = 2422 WHERE menu_id IN (2437, 2438); + +-- ============================================================= +-- 字典:客户业务类型 customer_type 补齐(原有仅 0直客) +-- 0直客 1同行 2集运 +-- ============================================================= +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 1, N'同行', '1', 'customer_type', '', 'info', 'N', '0', 'admin', sysdate(), '同行客户' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'customer_type' AND dict_value = '1'); +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 2, N'集运', '2', 'customer_type', '', 'warning', 'N', '0', 'admin', sysdate(), '集运客户' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'customer_type' AND dict_value = '2'); diff --git a/bms-front/src/api/customersuppliermanagement/customermoney/customerbill.js b/bms-front/src/api/customersuppliermanagement/customermoney/customerbill.js index be8ed613..626b1b90 100644 --- a/bms-front/src/api/customersuppliermanagement/customermoney/customerbill.js +++ b/bms-front/src/api/customersuppliermanagement/customermoney/customerbill.js @@ -35,19 +35,3 @@ export function cancelBalanceWriteoff(writeoffId, cancelRemark) { }) } -/** 账单查看:账单列表(只读) */ -export function listCustomerBill(query) { - return request({ - url: '/customersupplier/customerbill/list', - method: 'get', - params: query - }) -} - -/** 账单查看:账单详情(含明细) */ -export function getCustomerBill(arBillId) { - return request({ - url: '/customersupplier/customerbill/' + arBillId, - method: 'get' - }) -} diff --git a/bms-front/src/api/customersuppliermanagement/customermoney/customermoney.js b/bms-front/src/api/customersuppliermanagement/customermoney/customermoney.js index a9847d8e..9fb1bca4 100644 --- a/bms-front/src/api/customersuppliermanagement/customermoney/customermoney.js +++ b/bms-front/src/api/customersuppliermanagement/customermoney/customermoney.js @@ -68,11 +68,47 @@ export function listCreditFlow(query) { }) } -/** 调整客户额度(信用额度/临时额度),调整过程留额度流水 */ -export function adjustCredit(data) { +/** 提交额度调整申请(信用额度/临时额度),审批通过后才调整额度 */ +export function applyCreditAdjust(data) { return request({ - url: '/customersupplier/customermoney/credit/adjust', + url: '/customersupplier/customermoney/credit/apply', method: 'post', data: data }) } + +/** 查询额度调整申请列表 */ +export function listCreditApply(query) { + return request({ + url: '/customersupplier/customermoney/credit/apply/list', + method: 'get', + params: query + }) +} + +/** 审批通过额度调整申请 */ +export function approveCreditApply(applyId, approveRemark) { + return request({ + url: '/customersupplier/customermoney/credit/apply/approve/' + applyId, + method: 'put', + data: { approveRemark } + }) +} + +/** 驳回额度调整申请 */ +export function rejectCreditApply(applyId, approveRemark) { + return request({ + url: '/customersupplier/customermoney/credit/apply/reject/' + applyId, + method: 'put', + data: { approveRemark } + }) +} + +/** 撤销待审批的额度调整申请 */ +export function cancelCreditApply(applyId) { + return request({ + url: '/customersupplier/customermoney/credit/apply/cancel/' + applyId, + method: 'put' + }) +} + diff --git a/bms-front/src/views/ar/bill/index.vue b/bms-front/src/views/ar/bill/index.vue index 8210bd08..dde2a09c 100644 --- a/bms-front/src/views/ar/bill/index.vue +++ b/bms-front/src/views/ar/bill/index.vue @@ -1,6 +1,6 @@ - + @@ -804,7 +895,7 @@ import axios from 'axios' import { saveAs } from 'file-saver' import { getCustomer } from "@/api/customersuppliermanagement/customerinfo/customer.js" -import { listAccount, listReceipt, getReceipt, addReceipt, confirmReceipt, cancelReceipt, listFlow, listCreditFlow, adjustCredit } from "@/api/customersuppliermanagement/customermoney/customermoney.js" +import { listAccount, listReceipt, getReceipt, addReceipt, confirmReceipt, cancelReceipt, listFlow, listCreditFlow, applyCreditAdjust, listCreditApply, approveCreditApply, rejectCreditApply, cancelCreditApply } from "@/api/customersuppliermanagement/customermoney/customermoney.js" import { listUnsettledBill, listBalanceWriteoff, balanceWriteoff, cancelBalanceWriteoff } from "@/api/customersuppliermanagement/customermoney/customerbill.js" import { convertToBase } from "@/api/data/exchangeRate" import { getToken } from "@/utils/auth" @@ -1056,12 +1147,12 @@ function submitAdjustCredit() { payload.expireDate = f.expireDate || null } creditSubmitLoading.value = true - adjustCredit(payload).then(() => { - proxy.$modal.msgSuccess('额度调整成功') + applyCreditAdjust(payload).then(() => { + proxy.$modal.msgSuccess('申请已提交,等待审批') creditDialog.visible = false - loadCustomer() - loadAccounts() - getCreditFlowList() + if (creditApplyLoaded) { + getApplyList() + } }).finally(() => { creditSubmitLoading.value = false }) @@ -1346,6 +1437,88 @@ function resetCreditFlowQuery() { getCreditFlowList() } +/* ================= 额度调整申请/审批 ================= */ +const applyStatusOptions = [ + { value: 'PENDING', label: '待审批' }, + { value: 'APPROVED', label: '已通过' }, + { value: 'REJECTED', label: '已驳回' }, + { value: 'CANCELLED', label: '已撤销' } +] +const applyStatusTagMap = { PENDING: 'warning', APPROVED: 'success', REJECTED: 'danger', CANCELLED: 'info' } +function applyStatusTag(v) { + return applyStatusTagMap[v] || 'info' +} + +const applyList = ref([]) +const applyTotal = ref(0) +const applyLoading = ref(false) +const applyQuery = reactive({ + pageNum: 1, + pageSize: 10, + customerId: route.query.customerId, + creditType: '', + status: '' +}) +let creditApplyLoaded = false + +function getApplyList() { + const customerId = route.query.customerId + if (!customerId) return + applyLoading.value = true + applyQuery.customerId = customerId + listCreditApply({ ...applyQuery }).then(res => { + applyList.value = res.rows || [] + applyTotal.value = res.total || 0 + }).finally(() => { + applyLoading.value = false + }) +} + +function resetApplyQuery() { + applyQuery.creditType = '' + applyQuery.status = '' + applyQuery.pageNum = 1 + getApplyList() +} + +/** 审批通过后刷新账户卡片与额度流水 */ +function afterApplyHandled() { + getApplyList() + loadAccounts() + getCreditFlowList() +} + +function handleApproveApply(row) { + proxy.$modal.confirm(`确认通过额度调整申请「${row.applyNo}」?通过后将立即调整客户额度并写入额度流水。`) + .then(() => approveCreditApply(row.applyId, '审批通过')) + .then(() => { + proxy.$modal.msgSuccess('已审批通过') + afterApplyHandled() + }).catch(() => {}) +} + +function handleRejectApply(row) { + proxy.$prompt('请输入驳回原因', '驳回额度调整申请', { + confirmButtonText: '确认驳回', + cancelButtonText: '取消', + inputType: 'textarea', + inputPattern: /\S+/, + inputErrorMessage: '驳回原因不能为空' + }).then(({ value }) => rejectCreditApply(row.applyId, value)).then(() => { + proxy.$modal.msgSuccess('已驳回') + getApplyList() + }).catch(() => {}) +} + +function handleCancelApply(row) { + proxy.$modal.confirm(`确认撤销待审批申请「${row.applyNo}」?`) + .then(() => cancelCreditApply(row.applyId)) + .then(() => { + proxy.$modal.msgSuccess('已撤销') + getApplyList() + }).catch(() => {}) +} + /* ================= 账单核销 ================= */ const billStatusMap = { DRAFT: { label: '草稿', type: 'info' }, @@ -1366,7 +1539,7 @@ function formatDate(val) { } function goBillView() { router.push({ - path: '/customersuppliermanagement/customerbill', + path: '/ar/bill', query: { customerId: route.query.customerId || '' } }) } @@ -1510,6 +1683,10 @@ function submitCancelWriteoff() { } watch(activeTab, (tab) => { + if (tab === 'creditApply' && !creditApplyLoaded) { + creditApplyLoaded = true + getApplyList() + } if (tab === 'billWriteoff' && !billWriteoffLoaded) { billWriteoffLoaded = true loadBillWriteoffData() @@ -1632,6 +1809,13 @@ function initPage() { flowTotal.value = 0 creditFlowList.value = [] creditFlowTotal.value = 0 + applyList.value = [] + applyTotal.value = 0 + applyQuery.customerId = customerId + applyQuery.creditType = '' + applyQuery.status = '' + applyQuery.pageNum = 1 + creditApplyLoaded = false unsettledList.value = [] writeoffList.value = [] writeoffTotal.value = 0 @@ -1716,6 +1900,9 @@ watch(() => route.query.customerId, (newId, oldId) => { .credit-tip { margin-bottom: 12px; } +.credit-apply-tip { + margin-bottom: 12px; +} .credit-preview { margin-top: 4px; padding: 10px 14px; diff --git a/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue b/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue index e7782250..e8c0a5dd 100644 --- a/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue +++ b/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue @@ -124,7 +124,11 @@ - + + +