This commit is contained in:
wuweihua
2026-10-13 11:22:11 +08:00
7 changed files with 1655 additions and 4 deletions
@@ -0,0 +1,164 @@
package com.bms.web.controller.ar;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.ar.domain.ArBalanceWriteoff;
import com.bms.ar.domain.ArBill;
import com.bms.ar.service.IArBalanceWriteoffService;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.customer.customermoney.domain.CustomerReceipt;
import com.bms.customer.customermoney.service.ICustomerMoneyService;
/**
* 收款管理 Controller(账单管理-收款管理)
* <p>
* 收款登记/审核/取消与「客户余额-登记收款」复用同一套 Service 逻辑;
* 账单核销同样复用余额核销 Service:登记收款时可携带核销计划(审核通过后联动核销),
* 本页「账单核销」页签可直接对账单发起核销/反核销。
*
* @author bms
* @date 2026-10-13
*/
@RestController
@RequestMapping("/ar/receipt")
public class ArReceiptController extends BaseController
{
@Autowired
private ICustomerMoneyService customerMoneyService;
@Autowired
private IArBalanceWriteoffService balanceWriteoffService;
/** 收款时间查询/默认范围格式:精确到秒 */
private static final DateTimeFormatter DATE_TIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 查询收款记录(跨客户)。支持按客户、收款时间(精确到时分秒)等维度查询,未传时间范围时默认查当天。
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:list')")
@GetMapping("/list")
public TableDataInfo list(CustomerReceipt query)
{
if (query != null)
{
Object begin = query.getParams() == null ? null : query.getParams().get("beginTime");
Object end = query.getParams() == null ? null : query.getParams().get("endTime");
if (begin == null && end == null)
{
// 默认当天 00:00:00 ~ 23:59:59
LocalDate today = LocalDate.now();
query.getParams().put("beginTime", today.atStartOfDay().format(DATE_TIME_FORMATTER));
query.getParams().put("endTime", today.atTime(23, 59, 59).format(DATE_TIME_FORMATTER));
}
}
startPage();
List<CustomerReceipt> list = customerMoneyService.selectReceiptList(query);
return getDataTable(list);
}
/**
* 获取收款单详情(含核销计划与收款附件)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(customerMoneyService.selectReceiptById(id));
}
/**
* 登记收款(必须选择客户;默认待确认,审核通过后入账并执行登记时选择的核销计划)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:add')")
@Log(title = "收款管理-登记收款", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody CustomerReceipt receipt)
{
receipt.setCreateBy(getUsername());
return toAjax(customerMoneyService.insertReceipt(receipt));
}
/**
* 审核通过收款单:确认后金额计入客户余额,登记时选择的账单一并核销
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:review')")
@Log(title = "收款管理-收款审核", businessType = BusinessType.UPDATE)
@PutMapping("/confirm/{id}")
public AjaxResult confirm(@PathVariable("id") Long id)
{
return toAjax(customerMoneyService.confirmReceipt(id));
}
/**
* 取消收款单(仅待确认可取消)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:edit')")
@Log(title = "收款管理-取消收款", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{id}")
public AjaxResult cancel(@PathVariable("id") Long id)
{
return toAjax(customerMoneyService.cancelReceipt(id));
}
/**
* 查询客户可核销账单(已确认/部分核销,未核销额大于0),登记收款选账单与核销页签共用
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:list')")
@GetMapping("/bill/unsettled/{customerId}")
public AjaxResult unsettledBills(@PathVariable("customerId") Long customerId)
{
return success(balanceWriteoffService.selectUnsettledBills(customerId));
}
/**
* 查询核销记录(跨客户,支持客户/账单号/状态/核销日期范围)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:list')")
@GetMapping("/writeoff/list")
public TableDataInfo writeoffList(ArBalanceWriteoff query)
{
startPage();
List<ArBalanceWriteoff> list = balanceWriteoffService.selectWriteoffList(query);
return getDataTable(list);
}
/**
* 账单核销(账单核销页签直接核销;与客户余额页核销同一套逻辑)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:writeoff')")
@Log(title = "收款管理-账单核销", businessType = BusinessType.UPDATE)
@PostMapping("/writeoff")
public AjaxResult writeoff(@RequestBody ArBalanceWriteoff cmd)
{
cmd.setCreateBy(getUsername());
return success(balanceWriteoffService.writeoff(cmd));
}
/**
* 反核销(取消一笔有效核销,账单恢复未核销金额)
*/
@PreAuthorize("@ss.hasPermi('ar:receipt:writeoff:cancel')")
@Log(title = "收款管理-反核销", businessType = BusinessType.UPDATE)
@PutMapping("/writeoff/{writeoffId}/cancel")
public AjaxResult cancelWriteoff(@PathVariable("writeoffId") Long writeoffId,
@RequestBody(required = false) ArBalanceWriteoff body)
{
balanceWriteoffService.cancelWriteoff(writeoffId, body == null ? null : body.getCancelRemark());
return success();
}
}
@@ -51,6 +51,12 @@ public class CustomerReceipt extends BaseEntity
/** 客户ID */
private Long customerId;
/** 客户编码(列表关联查询,仅展示) */
private String customerCode;
/** 客户名称(列表关联查询,仅展示) */
private String customerName;
/** 客户账户ID(预留) */
private Long accountId;
@@ -83,6 +83,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="currency != null and currency != ''">and w.currency = #{currency}</if>
<if test="writeoffNo != null and writeoffNo != ''">and w.writeoff_no like concat('%', #{writeoffNo}, '%')</if>
<if test="billNo != null and billNo != ''">and w.bill_no like concat('%', #{billNo}, '%')</if>
<if test="receiptId != null">and w.receipt_id = #{receiptId}</if>
<if test="params != null and params.beginTime != null and params.beginTime != ''">
and date_format(w.writeoff_date,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params != null and params.endTime != null and params.endTime != ''">
and date_format(w.writeoff_date,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
</where>
order by w.create_time desc, w.writeoff_id desc
</select>
@@ -236,6 +236,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<id property="id" column="id" />
<result property="receiptNo" column="receipt_no" />
<result property="customerId" column="customer_id" />
<result property="customerCode" column="customer_code" />
<result property="customerName" column="customer_name" />
<result property="accountId" column="account_id" />
<result property="receiptType" column="receipt_type" />
<result property="amount" column="amount" />
@@ -268,7 +270,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectCustomerReceiptVo">
select r.id, r.receipt_no, r.customer_id, r.account_id, r.receipt_type, r.amount, r.allocated_amount,
select r.id, r.receipt_no, r.customer_id, c.customer_code, c.customer_name, r.account_id, r.receipt_type,
r.amount, r.allocated_amount,
r.unallocated_amount, r.currency, r.exchange_rate, r.rate_source, r.rate_source_desc,
r.base_amount, r.pay_method, r.bank_account_id, r.bank_account_name,
r.bank_account_no, r.bank_name, r.bank_transaction_no,
@@ -278,6 +281,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
(select count(1) from ctr_customer_attachment a
where a.biz_id = r.id and a.attachment_type = '3' and a.del_flag = '0') as attachment_count
from ctr_customer_receipt r
left join ctr_customer c on c.customer_id = r.customer_id
</sql>
<select id="selectReceiptList" parameterType="com.bms.customer.customermoney.domain.CustomerReceipt"
@@ -286,7 +290,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<where>
r.del_flag = '0'
<if test="customerId != null"> and r.customer_id = #{customerId}</if>
<if test="customerCode != null and customerCode != ''"> and c.customer_code like concat('%', #{customerCode}, '%')</if>
<if test="customerName != null and customerName != ''"> and c.customer_name like concat('%', #{customerName}, '%')</if>
<if test="receiptNo != null and receiptNo != ''"> and r.receipt_no like concat('%', #{receiptNo}, '%')</if>
<if test="receiptType != null and receiptType != ''"> and r.receipt_type = #{receiptType}</if>
<if test="status != null and status != ''"> and r.status = #{status}</if>
@@ -295,11 +300,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="bankTransactionNo != null and bankTransactionNo != ''">
and r.bank_transaction_no like concat('%', #{bankTransactionNo}, '%')
</if>
<!-- 收款时间精确到时分秒:入参为 yyyy-MM-dd HH:mm:ss 直接比较;
兼容客户余额页传入的纯日期 yyyy-MM-dd(开始补 00:00:00、结束补 23:59:59) -->
<if test="params != null and params.beginTime != null and params.beginTime != ''">
and date_format(r.received_at,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
and r.received_at &gt;= concat(#{params.beginTime},
case when char_length(#{params.beginTime}) = 10 then ' 00:00:00' else '' end)
</if>
<if test="params != null and params.endTime != null and params.endTime != ''">
and date_format(r.received_at,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
and r.received_at &lt;= concat(#{params.endTime},
case when char_length(#{params.endTime}) = 10 then ' 23:59:59' else '' end)
</if>
</where>
order by r.received_at desc, r.id desc
+44
View File
@@ -0,0 +1,44 @@
-- ============================================================
-- 账单管理 - 收款管理 菜单及权限
-- 日期:2026-10-13
-- 说明:
-- 1) 在「账单管理」(menu_id=2400) 下新增「收款管理」页面(应收账单 order=1 之后,应收日志 order=3 之前);
-- 2) 页面含两个页签:收款记录(跨客户查询/默认当天/勾选汇总收款总额/登记收款/审核/取消/查看)、
-- 账单核销(账单查询并直接核销、核销记录查询与反核销;登记收款时同样可携带核销计划,审核后联动核销);
-- 3) 收款登记/审核/取消与「客户余额-登记收款」复用同一套后端 Service 逻辑(ctr_customer_receipt);
-- 4) 角色授权与「应收账单」(2401) 当前可见角色保持一致(role_id=6040 销售经理);
-- 超级管理员(admin)默认拥有全部权限;其他角色可在 系统管理-角色 中勾选「收款管理」。
-- ============================================================
-- 1. 收款管理 页面菜单
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)
values (2461, '收款管理', 2400, 2, 'receipt', 'ar/receipt/index', null, 'ArReceipt',
1, 0, 'C', '0', '0', 'ar:receipt:list', 'validCode',
'admin', sysdate(), '收款记录查询、登记收款与账单核销');
-- 2. 按钮权限
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)
values
(2462, '收款查看', 2461, 1, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:query', '#', 'admin', sysdate(), '收款单详情(含核销计划与凭证)'),
(2463, '登记收款', 2461, 2, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:add', '#', 'admin', sysdate(), '登记客户收款(必选客户,可同时选择核销账单)'),
(2464, '收款审核', 2461, 3, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:review', '#', 'admin', sysdate(), '审核通过收款单,入账并联动核销'),
(2465, '取消收款', 2461, 4, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:edit', '#', 'admin', sysdate(), '取消待确认收款单'),
(2466, '账单核销', 2461, 5, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:writeoff', '#', 'admin', sysdate(), '账单核销页签直接核销应收账单'),
(2467, '核销反核销', 2461, 6, '', null, null, '', 1, 0, 'F', '0', '0', 'ar:receipt:writeoff:cancel', '#', 'admin', sysdate(), '反核销账单核销记录');
-- 3. 授权给拥有「应收账单」菜单的角色(当前为 role_id=6040 销售经理)
insert into sys_role_menu(role_id, menu_id)
select r.role_id, m.menu_id
from sys_role r
cross join (
select 2461 as menu_id union all select 2462 union all select 2463 union all
select 2464 union all select 2465 union all select 2466 union all select 2467
) m
where r.role_id in (select role_id from sys_role_menu where menu_id = 2401)
and not exists (
select 1 from sys_role_menu rm where rm.role_id = r.role_id and rm.menu_id = m.menu_id
);
+50
View File
@@ -0,0 +1,50 @@
import request from '@/utils/request'
/** 查询收款记录(跨客户,未传收款时间时后端默认当天) */
export function listReceipt(query) {
return request({ url: '/ar/receipt/list', method: 'get', params: query })
}
/** 收款单详情(含核销计划与凭证附件) */
export function getReceipt(id) {
return request({ url: '/ar/receipt/' + id, method: 'get' })
}
/** 登记收款(必须选择客户,与客户余额-登记收款同一套逻辑) */
export function addReceipt(data) {
return request({ url: '/ar/receipt', method: 'post', data })
}
/** 审核通过收款单 */
export function confirmReceipt(id) {
return request({ url: '/ar/receipt/confirm/' + id, method: 'put' })
}
/** 取消收款单 */
export function cancelReceipt(id) {
return request({ url: '/ar/receipt/cancel/' + id, method: 'put' })
}
/** 查询客户可核销账单(已确认/部分核销) */
export function listUnsettledBill(customerId) {
return request({ url: '/ar/receipt/bill/unsettled/' + customerId, method: 'get' })
}
/** 查询核销记录(跨客户) */
export function listReceiptWriteoff(query) {
return request({ url: '/ar/receipt/writeoff/list', method: 'get', params: query })
}
/** 账单核销(账单核销页签直接核销) */
export function receiptWriteoff(data) {
return request({ url: '/ar/receipt/writeoff', method: 'post', data })
}
/** 反核销 */
export function cancelReceiptWriteoff(writeoffId, cancelRemark) {
return request({
url: '/ar/receipt/writeoff/' + writeoffId + '/cancel',
method: 'put',
data: { cancelRemark }
})
}
File diff suppressed because it is too large Load Diff