核销记录额度变化
This commit is contained in:
+44
-2
@@ -376,9 +376,10 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
|||||||
BigDecimal afterBalance = beforeBalance.subtract(changeAmount);
|
BigDecimal afterBalance = beforeBalance.subtract(changeAmount);
|
||||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||||
account.setBalance(afterBalance);
|
account.setBalance(afterBalance);
|
||||||
|
BigDecimal beforeUsedCredit = nz(account.getUsedCredit());
|
||||||
if (Boolean.TRUE.equals(command.getReleaseUsedCredit()))
|
if (Boolean.TRUE.equals(command.getReleaseUsedCredit()))
|
||||||
{
|
{
|
||||||
account.setUsedCredit(nz(account.getUsedCredit()).subtract(changeAmount).max(BigDecimal.ZERO)
|
account.setUsedCredit(beforeUsedCredit.subtract(changeAmount).max(BigDecimal.ZERO)
|
||||||
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
||||||
}
|
}
|
||||||
account.setUpdateBy(username);
|
account.setUpdateBy(username);
|
||||||
@@ -390,6 +391,18 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
|||||||
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, -1,
|
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, -1,
|
||||||
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
||||||
customerMoneyMapper.insertFlow(flow);
|
customerMoneyMapper.insertFlow(flow);
|
||||||
|
|
||||||
|
// 核销扣款联动释放已用额度:补写额度流水(RELEASE),保证 used_credit 变动台账完整
|
||||||
|
BigDecimal afterUsedCredit = nz(account.getUsedCredit());
|
||||||
|
if (Boolean.TRUE.equals(command.getReleaseUsedCredit())
|
||||||
|
&& afterUsedCredit.compareTo(beforeUsedCredit) < 0)
|
||||||
|
{
|
||||||
|
writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, null),
|
||||||
|
CustomerCreditFlow.CHANGE_RELEASE, -1,
|
||||||
|
beforeUsedCredit, afterUsedCredit,
|
||||||
|
beforeUsedCredit.subtract(afterUsedCredit),
|
||||||
|
username, userId);
|
||||||
|
}
|
||||||
return flow;
|
return flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,10 +425,11 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
|||||||
BigDecimal afterBalance = beforeBalance.add(changeAmount);
|
BigDecimal afterBalance = beforeBalance.add(changeAmount);
|
||||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||||
account.setBalance(afterBalance);
|
account.setBalance(afterBalance);
|
||||||
|
BigDecimal beforeUsedCredit = nz(account.getUsedCredit());
|
||||||
if (Boolean.TRUE.equals(command.getReoccupyUsedCredit()))
|
if (Boolean.TRUE.equals(command.getReoccupyUsedCredit()))
|
||||||
{
|
{
|
||||||
// 反核销回补:账单恢复未清偿,授信占用同步加回(允许出现高于总额度的占用存量)
|
// 反核销回补:账单恢复未清偿,授信占用同步加回(允许出现高于总额度的占用存量)
|
||||||
account.setUsedCredit(nz(account.getUsedCredit()).add(changeAmount)
|
account.setUsedCredit(beforeUsedCredit.add(changeAmount)
|
||||||
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
||||||
}
|
}
|
||||||
account.setUpdateBy(username);
|
account.setUpdateBy(username);
|
||||||
@@ -427,11 +441,39 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
|||||||
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, 1,
|
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, 1,
|
||||||
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
||||||
customerMoneyMapper.insertFlow(flow);
|
customerMoneyMapper.insertFlow(flow);
|
||||||
|
|
||||||
|
// 反核销回补联动重新占用已用额度:补写额度流水(OCCUPY),与核销释放流水方向相反、可勾稽
|
||||||
|
BigDecimal afterUsedCredit = nz(account.getUsedCredit());
|
||||||
|
if (Boolean.TRUE.equals(command.getReoccupyUsedCredit())
|
||||||
|
&& afterUsedCredit.compareTo(beforeUsedCredit) > 0)
|
||||||
|
{
|
||||||
|
String reoccupyReason = "反核销重新占用授信额度"
|
||||||
|
+ (StringUtils.hasText(command.getBizNo()) ? ":" + command.getBizNo() : "");
|
||||||
|
writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, reoccupyReason),
|
||||||
|
CustomerCreditFlow.CHANGE_OCCUPY, 1,
|
||||||
|
beforeUsedCredit, afterUsedCredit,
|
||||||
|
afterUsedCredit.subtract(beforeUsedCredit),
|
||||||
|
username, userId);
|
||||||
|
}
|
||||||
return flow;
|
return flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 内部实现 ================= */
|
/* ================= 内部实现 ================= */
|
||||||
|
|
||||||
|
/** 余额变动命令 -> 额度占用命令(复用业务单据信息,金额取实际联动额由调用方写流水时传入) */
|
||||||
|
private CreditUsageCommand toCreditUsageCommand(BalanceChangeCommand source, String accountCurrency, String reason)
|
||||||
|
{
|
||||||
|
CreditUsageCommand target = new CreditUsageCommand();
|
||||||
|
target.setCustomerId(source.getCustomerId());
|
||||||
|
target.setCurrency(accountCurrency);
|
||||||
|
target.setAmount(money(source.getAmount()));
|
||||||
|
target.setBizType(source.getBizType());
|
||||||
|
target.setBizId(source.getBizId());
|
||||||
|
target.setBizNo(source.getBizNo());
|
||||||
|
target.setReason(reason);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
/** 额度占用/释放写额度流水(before/after 为已用额度快照) */
|
/** 额度占用/释放写额度流水(before/after 为已用额度快照) */
|
||||||
private CustomerCreditFlow writeUsedCreditFlow(CustomerAccount account, CreditUsageCommand command,
|
private CustomerCreditFlow writeUsedCreditFlow(CustomerAccount account, CreditUsageCommand command,
|
||||||
String changeType, int direction, BigDecimal beforeUsed, BigDecimal afterUsed,
|
String changeType, int direction, BigDecimal beforeUsed, BigDecimal afterUsed,
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-popover :width="240" placement="bottom-end" trigger="click">
|
<el-popover :width="240" placement="bottom-end" trigger="click" popper-class="column-settings-popper">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button circle plain icon="Operation" title="列设置" aria-label="列设置" />
|
<el-button circle plain icon="Operation" title="列设置" />
|
||||||
</template>
|
</template>
|
||||||
<div class="column-settings">
|
<div class="column-settings">
|
||||||
<div class="column-settings__header">
|
<div class="column-settings__header">
|
||||||
<span class="column-settings__title">列展示设置</span>
|
<span class="column-settings__title">列展示设置</span>
|
||||||
<el-button link type="primary" size="small" @click="emit('reset')">恢复默认</el-button>
|
<el-button link type="primary" size="small" @click="emit('reset')">恢复默认</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-checkbox-group v-model="checkedKeys" class="column-settings__body">
|
<el-checkbox-group :model-value="checkedKeys" @change="onChange" class="column-settings__body">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-for="c in columns"
|
v-for="c in columns"
|
||||||
:key="c.key"
|
:key="c.key"
|
||||||
@@ -23,25 +23,29 @@
|
|||||||
<script setup name="ColumnSettings">
|
<script setup name="ColumnSettings">
|
||||||
/**
|
/**
|
||||||
* 表格列设置弹窗(与 useColumnSettings 配合):
|
* 表格列设置弹窗(与 useColumnSettings 配合):
|
||||||
* <column-settings :columns="columnDefs" :visible-map="colVisible" @reset="resetColumns" />
|
* <column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||||
|
* 注意:reference 不要嵌套 el-tooltip——两个 popper 组件嵌套会导致引用解析异常。
|
||||||
*/
|
*/
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
columns: { type: Array, default: () => [] },
|
columns: { type: Array, default: () => [] },
|
||||||
visibleMap: { type: Object, required: true }
|
visibleMap: { type: Object, required: true }
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['reset'])
|
const emit = defineEmits(['toggle', 'reset'])
|
||||||
|
|
||||||
const checkedKeys = computed({
|
const checkedKeys = computed(() =>
|
||||||
get() {
|
props.columns.filter(c => props.visibleMap[c.key] !== false).map(c => c.key)
|
||||||
return props.columns.filter(c => props.visibleMap[c.key] !== false).map(c => c.key)
|
)
|
||||||
},
|
|
||||||
set(keys) {
|
/** el-checkbox-group 回传当前全量勾选值,逐列 diff 后通知父级修改 */
|
||||||
props.columns.forEach(c => {
|
function onChange(keys) {
|
||||||
if (c.fixed) return
|
props.columns.forEach(c => {
|
||||||
props.visibleMap[c.key] = keys.includes(c.key)
|
if (c.fixed) return
|
||||||
})
|
const next = keys.includes(c.key)
|
||||||
}
|
if ((props.visibleMap[c.key] !== false) !== next) {
|
||||||
})
|
emit('toggle', c.key, next)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import hasRole from './permission/hasRole'
|
import hasRole from './permission/hasRole'
|
||||||
import hasPermi from './permission/hasPermi'
|
import hasPermi from './permission/hasPermi'
|
||||||
import copyText from './common/copyText'
|
import copyText from './common/copyText'
|
||||||
import resizableColumn from './resizableColumn'
|
|
||||||
|
|
||||||
export default function directive(app){
|
export default function directive(app){
|
||||||
app.directive('hasRole', hasRole)
|
app.directive('hasRole', hasRole)
|
||||||
app.directive('hasPermi', hasPermi)
|
app.directive('hasPermi', hasPermi)
|
||||||
app.directive('copyText', copyText)
|
app.directive('copyText', copyText)
|
||||||
app.directive('resizable', resizableColumn)
|
|
||||||
}
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/**
|
|
||||||
* v-resizable —— 让 el-table 表头右边界可拖拽调整列宽。
|
|
||||||
*
|
|
||||||
* 原理:在表格根元素上做事件委托。当鼠标点按落在表头某个 th 右边界(约 8px 内)时,进入拖拽;
|
|
||||||
* 拖拽过程中动态改写该列对应 <col> 与 th 的 width,从而整列变宽/变窄(跨表头与数据区)。
|
|
||||||
* 不依赖 el-table 原生的 .el-table__column-resizer(当前运行环境未渲染,故自实现)。
|
|
||||||
*
|
|
||||||
* 用法:<el-table v-resizable border :data="..."> ... </el-table>
|
|
||||||
*/
|
|
||||||
const EDGE = 8 // 判定"右边界"的像素容差
|
|
||||||
|
|
||||||
function colIndex(th) {
|
|
||||||
const row = th.parentNode
|
|
||||||
return Array.prototype.indexOf.call(row.children, th)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同时改写表头与表体的列宽。
|
|
||||||
* el-table 将表头和表体渲染为两张独立的 <table>(各自带 colgroup),
|
|
||||||
* 仅改一张会导致表头与数据行错位,因此须对主结构(.el-table__inner-wrapper)里的所有 table 一并更新。
|
|
||||||
*/
|
|
||||||
function applyWidth(scopeEl, idx, w) {
|
|
||||||
scopeEl.querySelectorAll('table').forEach(tbl => {
|
|
||||||
// 跳过固定附表(fixed 列独立渲染,索引语义不同,避免误改)
|
|
||||||
if (tbl.closest('.el-table__fixed, .el-table-fixed-column')) return
|
|
||||||
const col = tbl.querySelector('colgroup') && tbl.querySelector('colgroup').children[idx]
|
|
||||||
if (col) col.style.width = w + 'px'
|
|
||||||
tbl.querySelectorAll('tr').forEach(tr => {
|
|
||||||
const cell = tr.children[idx]
|
|
||||||
if (!cell) return
|
|
||||||
cell.style.width = w + 'px'
|
|
||||||
if (cell.tagName === 'TH') {
|
|
||||||
cell.style.minWidth = ''
|
|
||||||
cell.style.maxWidth = ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindDrag(tableEl) {
|
|
||||||
let dragging = null
|
|
||||||
|
|
||||||
function onMouseMove(e) {
|
|
||||||
if (!dragging) return
|
|
||||||
const dx = e.clientX - dragging.startX
|
|
||||||
const w = Math.max(dragging.minWidth || 60, dragging.startWidth + dx)
|
|
||||||
applyWidth(tableEl, dragging.idx, w)
|
|
||||||
dragging.lastWidth = w
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseUp() {
|
|
||||||
if (dragging) {
|
|
||||||
document.removeEventListener('mousemove', onMouseMove)
|
|
||||||
document.removeEventListener('mouseup', onMouseUp)
|
|
||||||
document.body.style.cursor = ''
|
|
||||||
document.body.style.userSelect = ''
|
|
||||||
dragging = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tableEl.addEventListener('mousedown', (e) => {
|
|
||||||
const target = e.target
|
|
||||||
const th = target.closest('th')
|
|
||||||
if (!th || !tableEl.contains(th)) return
|
|
||||||
// 固定列(fixed left/right)不参与拖拽
|
|
||||||
if (th.classList.contains('el-table-fixed-column') || th.className.indexOf('fixed') > -1) return
|
|
||||||
|
|
||||||
const rect = th.getBoundingClientRect()
|
|
||||||
if (e.clientX < rect.right - EDGE) return // 只有贴近右边界才触发
|
|
||||||
|
|
||||||
const idx = colIndex(th)
|
|
||||||
|
|
||||||
dragging = {
|
|
||||||
startX: e.clientX,
|
|
||||||
startWidth: rect.width,
|
|
||||||
minWidth: Number(th.dataset.resizableMin) || 40,
|
|
||||||
idx,
|
|
||||||
th
|
|
||||||
}
|
|
||||||
document.body.style.cursor = 'col-resize'
|
|
||||||
document.body.style.userSelect = 'none'
|
|
||||||
document.addEventListener('mousemove', onMouseMove)
|
|
||||||
document.addEventListener('mouseup', onMouseUp)
|
|
||||||
e.preventDefault()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mounted(el) {
|
|
||||||
bindDrag(el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,11 +40,18 @@ export function useColumnSettings(storageKey, columns) {
|
|||||||
return colVisible[key] !== false
|
return colVisible[key] !== false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 单列显隐切换(供 ColumnSettings 组件 @toggle 调用) */
|
||||||
|
function toggleColumn(key, visible) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(colVisible, key) || columns.some(c => c.key === key)) {
|
||||||
|
colVisible[key] = !!visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function resetColumns() {
|
function resetColumns() {
|
||||||
columns.forEach(c => {
|
columns.forEach(c => {
|
||||||
colVisible[c.key] = c.fixed ? true : c.defaultVisible !== false
|
colVisible[c.key] = c.fixed ? true : c.defaultVisible !== false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return { colVisible, isColVisible, resetColumns }
|
return { colVisible, isColVisible, toggleColumn, resetColumns }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,10 +115,10 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
<column-settings :columns="columnDefs" :visible-map="colVisible" @reset="resetColumns" />
|
<column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" v-resizable border :data="customerList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" border :data="customerList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||||
<!-- <el-table-column label="客户ID" align="center" prop="customerId" />-->
|
<!-- <el-table-column label="客户ID" align="center" prop="customerId" />-->
|
||||||
<el-table-column v-if="isColVisible('customerCode')" label="客户编码" align="center" prop="customerCode" />
|
<el-table-column v-if="isColVisible('customerCode')" label="客户编码" align="center" prop="customerCode" />
|
||||||
@@ -225,7 +225,7 @@ const columnDefs = [
|
|||||||
{ key: 'productIds', label: '绑定产品' },
|
{ key: 'productIds', label: '绑定产品' },
|
||||||
{ key: 'remark', label: '备注说明' }
|
{ key: 'remark', label: '备注说明' }
|
||||||
]
|
]
|
||||||
const { colVisible, isColVisible, resetColumns } = useColumnSettings('customer-info-list', columnDefs)
|
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('customer-info-list', columnDefs)
|
||||||
|
|
||||||
const customerList = ref([])
|
const customerList = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|||||||
@@ -96,10 +96,10 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
<column-settings :columns="columnDefs" :visible-map="colVisible" @reset="resetColumns" />
|
<column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" v-resizable border :data="supplierList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" border :data="supplierList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||||
<el-table-column v-if="isColVisible('supplierCode')" label="供应商编码" align="center" prop="supplierCode" />
|
<el-table-column v-if="isColVisible('supplierCode')" label="供应商编码" align="center" prop="supplierCode" />
|
||||||
<el-table-column v-if="isColVisible('supplierName')" label="供应商名称" align="center" prop="supplierName" :show-overflow-tooltip="true" />
|
<el-table-column v-if="isColVisible('supplierName')" label="供应商名称" align="center" prop="supplierName" :show-overflow-tooltip="true" />
|
||||||
@@ -192,7 +192,7 @@ const columnDefs = [
|
|||||||
{ key: 'channelIds', label: '绑定渠道ID' },
|
{ key: 'channelIds', label: '绑定渠道ID' },
|
||||||
{ key: 'remark', label: '备注说明' }
|
{ key: 'remark', label: '备注说明' }
|
||||||
]
|
]
|
||||||
const { colVisible, isColVisible, resetColumns } = useColumnSettings('supplier-info-list', columnDefs)
|
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('supplier-info-list', columnDefs)
|
||||||
|
|
||||||
const supplierList = ref([])
|
const supplierList = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|||||||
Reference in New Issue
Block a user