登记收款选择账单直接核销,附件上传支持拖动上传
This commit is contained in:
+265
-7
@@ -531,7 +531,12 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="writeoffList" border size="small" v-loading="writeoffLoading">
|
||||
<el-table-column label="核销单号" align="center" prop="writeoffNo" width="180" />
|
||||
<el-table-column label="核销单号" align="center" width="190">
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.writeoffNo }}</div>
|
||||
<el-tag v-if="scope.row.receiptId" type="primary" size="small" effect="plain" style="margin-top: 2px;">收款联动</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="账单号" align="center" prop="billNo" width="170" show-overflow-tooltip />
|
||||
<el-table-column label="账单名称" align="center" prop="billName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="核销金额" align="center" width="120">
|
||||
@@ -574,7 +579,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- ====== 登记收款弹窗 ====== -->
|
||||
<el-dialog title="登记收款" v-model="receiptDialog.visible" width="640px" append-to-body @closed="onReceiptDialogClosed">
|
||||
<el-dialog title="登记收款" v-model="receiptDialog.visible" width="780px" append-to-body @closed="onReceiptDialogClosed">
|
||||
<el-form ref="receiptFormRef" :model="receiptDialog.form" :rules="receiptRules" label-width="110px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
@@ -665,6 +670,54 @@
|
||||
<el-input v-model="receiptDialog.form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="核销账单">
|
||||
<div class="receipt-wo-toolbar">
|
||||
<el-button type="primary" plain icon="Plus" @click="openBillPicker">选择账单核销</el-button>
|
||||
<span class="receipt-wo-summary">
|
||||
本次核销合计:<b class="money-out">{{ formatMoney(receiptWriteoffTotal) }} CNY</b>
|
||||
,收款折算本位币:<b>{{ formatMoney(receiptBaseCny) }} CNY</b>
|
||||
<!-- ,核销后剩余入账:<b :class="receiptWriteoffRemain < 0 ? 'money-out' : 'money-in'">-->
|
||||
<!-- {{ formatMoney(receiptWriteoffRemain) }} CNY-->
|
||||
<!-- </b>-->
|
||||
</span>
|
||||
</div>
|
||||
<el-table :data="receiptDialog.form.writeoffPlans" border size="small" max-height="220">
|
||||
<el-table-column label="账单号" prop="billNo" align="center" width="170" show-overflow-tooltip />
|
||||
<el-table-column label="账单名称" prop="billName" align="center" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="未核销金额" align="center" width="120">
|
||||
<template #default="scope">{{ formatMoney(scope.row.unwrittenAmount) }} {{ scope.row.currency }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="本次核销金额(CNY)" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.writeoffAmount"
|
||||
:min="0"
|
||||
:max="Number(scope.row.unwrittenAmount)"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
:controls="false"
|
||||
size="small"
|
||||
style="width: 130px;"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="70">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" icon="Delete" @click="removeReceiptBill(scope.$index)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<span style="color: var(--el-text-color-secondary);">不选账单则整笔计入客户余额,可在“账单核销”页手工核销</span>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="upload-tip">
|
||||
当前仅支持<strong>人民币(CNY)账单</strong>核销,账单行金额均按 CNY 填写;
|
||||
所选账单在收款单<strong>审核通过</strong>后一并核销,核销合计不能超过收款金额按汇率折算的本位币金额
|
||||
({{ formatMoney(receiptBaseCny) }} CNY);<strong>核销不扣减客户余额</strong>,正常计入客户余额。
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="收款凭证">
|
||||
<el-upload
|
||||
@@ -747,6 +800,27 @@
|
||||
<el-descriptions-item label="备注" :span="2">{{ viewDialog.data.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<template v-if="viewDialog.data && viewDialog.data.writeoffPlans && viewDialog.data.writeoffPlans.length">
|
||||
<div class="attach-title">关联核销账单({{ viewDialog.data.writeoffPlans.length }})</div>
|
||||
<el-table :data="viewDialog.data.writeoffPlans" border size="small" max-height="220" style="margin-bottom: 12px;">
|
||||
<el-table-column label="账单号" prop="billNo" align="center" width="170" show-overflow-tooltip />
|
||||
<el-table-column label="账单名称" prop="billName" align="center" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="本次核销金额" align="center" width="140">
|
||||
<template #default="scope">{{ formatMoney(scope.row.writeoffAmount) }} {{ scope.row.currency }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiptPlanStatusTag(scope.row.status)" size="small">
|
||||
{{ receiptPlanStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="核销日期" align="center" width="110">
|
||||
<template #default="scope">{{ scope.row.writeoffDate ? formatDate(scope.row.writeoffDate) : '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<div class="attach-title">收款凭证附件</div>
|
||||
<el-table :data="viewDialog.attachments" border size="small" max-height="240" v-loading="viewDialog.loading">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
@@ -913,6 +987,49 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 登记收款-选择核销账单弹窗 ====== -->
|
||||
<el-dialog title="选择待核销账单" v-model="billPicker.visible" width="860px" append-to-body>
|
||||
<el-alert type="info" :closable="false" class="writeoff-summary">
|
||||
当前仅支持人民币(CNY)账单核销,以下为状态已确认/部分核销的 CNY 账单;
|
||||
已选 {{ billPicker.selected.length }} 张,合计未核销
|
||||
<b class="money-out">{{ formatMoney(billPickerSelectedTotal) }} CNY</b>,
|
||||
本收款单折算可核销上限 <b>{{ formatMoney(receiptBaseCny) }} CNY</b>
|
||||
</el-alert>
|
||||
<el-table
|
||||
ref="billPickerTableRef"
|
||||
:data="billPicker.rows"
|
||||
border
|
||||
size="small"
|
||||
max-height="380"
|
||||
row-key="arBillId"
|
||||
v-loading="billPicker.loading"
|
||||
@selection-change="onPickerSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="42" align="center" reserve-selection />
|
||||
<el-table-column label="账单号" prop="billNo" align="center" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="账单名称" prop="billName" align="center" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="账单日期" prop="billDate" align="center" width="105">
|
||||
<template #default="scope">{{ formatDate(scope.row.billDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应收金额" align="center" width="110">
|
||||
<template #default="scope">{{ formatMoney(scope.row.totalAmount) }} {{ scope.row.currency }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已核销" align="center" width="100">
|
||||
<template #default="scope"><span class="money-in">{{ formatMoney(scope.row.writtenOffAmount) }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未核销" align="center" width="100">
|
||||
<template #default="scope"><span class="money-out">{{ formatMoney(scope.row.unwrittenAmount) }}</span></template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="该币种暂无可核销账单" :image-size="70" />
|
||||
</template>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="confirmBillPicker">确 定({{ billPicker.selected.length }})</el-button>
|
||||
<el-button @click="billPicker.visible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 余额核销账单弹窗 ====== -->
|
||||
<el-dialog title="余额核销账单" v-model="woDialog.visible" width="560px" append-to-body v-loading="woDialog.accountLoading">
|
||||
<el-descriptions :column="1" border size="small" v-if="woDialog.bill">
|
||||
@@ -966,7 +1083,10 @@
|
||||
<el-alert type="warning" :closable="false" class="credit-tip">
|
||||
<span v-if="cancelWoDialog.row">
|
||||
反核销后,账单「{{ cancelWoDialog.row.billNo }}」恢复为未核销状态(核销金额 CNY {{ formatMoney(cancelWoDialog.row.writeoffAmount) }});
|
||||
口径变更后不再回补账户余额、不再变动已用额度。
|
||||
不回补账户余额、不变动已用额度。
|
||||
<template v-if="cancelWoDialog.row.receiptId">
|
||||
<br />该核销由收款单审核<strong>联动产生</strong>,反核销后收款单中本笔将回到“未分配”金额,可重新核销其他账单,已入账余额不受影响。
|
||||
</template>
|
||||
</span>
|
||||
</el-alert>
|
||||
<el-form label-width="100px" class="wo-form">
|
||||
@@ -1386,12 +1506,13 @@ function handleExportCreditFlow() {
|
||||
|
||||
/** 审核通过 */
|
||||
function handleConfirmReceipt(row) {
|
||||
proxy.$modal.confirm(`是否确认审核通过收款单「${row.receiptNo}」?审核通过后金额将计入客户余额。`).then(() => {
|
||||
proxy.$modal.confirm(`是否确认审核通过收款单「${row.receiptNo}」?审核通过后金额计入客户余额,登记时选择的账单将一并核销。`).then(() => {
|
||||
return confirmReceipt(row.id)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("审核通过,余额已更新")
|
||||
proxy.$modal.msgSuccess("审核通过,余额与账单核销已更新")
|
||||
getReceiptList()
|
||||
loadAccounts()
|
||||
loadBillWriteoffData()
|
||||
if (activeTab.value === 'flow') getFlowList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
@@ -1434,7 +1555,8 @@ function resetReceiptForm() {
|
||||
bankAccountNo: '',
|
||||
bankTransactionNo: '',
|
||||
remark: '',
|
||||
attachmentList: []
|
||||
attachmentList: [],
|
||||
writeoffPlans: []
|
||||
}
|
||||
proxy.resetForm('receiptFormRef')
|
||||
}
|
||||
@@ -1476,7 +1598,7 @@ function fetchDefaultRate() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 切换币种:重置手工标记并重新取默认汇率 */
|
||||
/** 切换币种:重置手工标记并重新取默认汇率;核销账单固定为CNY,只需按新汇率重算可核销上限,不清空已选 */
|
||||
function handleCurrencyChange() {
|
||||
rateEdited.value = false
|
||||
rateSourceInfo.value = { source: '', ratePath: '' }
|
||||
@@ -1502,6 +1624,130 @@ watch(() => receiptDialog.form.receivedAt, () => {
|
||||
}
|
||||
})
|
||||
|
||||
/* ============ 登记收款-选择核销账单 ============ */
|
||||
const billPickerTableRef = ref(null)
|
||||
const billPicker = reactive({
|
||||
visible: false,
|
||||
loading: false,
|
||||
rows: [],
|
||||
selected: []
|
||||
})
|
||||
|
||||
/** 收款金额按汇率折算的本位币金额(CNY),即核销合计上限 */
|
||||
const receiptBaseCny = computed(() => {
|
||||
const amt = Number(receiptDialog.form.amount) || 0
|
||||
const rate = Number(receiptDialog.form.exchangeRate) || 0
|
||||
return Math.round(amt * rate * 100) / 100
|
||||
})
|
||||
/** 已分配合计(账单均为CNY,此值即 CNY) */
|
||||
const receiptWriteoffTotal = computed(() => {
|
||||
const list = receiptDialog.form.writeoffPlans || []
|
||||
return list.reduce((s, r) => s + (Number(r.writeoffAmount) || 0), 0)
|
||||
})
|
||||
/** 核销后剩余入账金额(折算CNY - 已分配CNY) */
|
||||
const receiptWriteoffRemain = computed(() => {
|
||||
return Math.round((receiptBaseCny.value - receiptWriteoffTotal.value) * 100) / 100
|
||||
})
|
||||
/** 选择器中勾选账单的未核销额合计(仅提示) */
|
||||
const billPickerSelectedTotal = computed(() => {
|
||||
return billPicker.selected.reduce((s, r) => s + (Number(r.unwrittenAmount) || 0), 0)
|
||||
})
|
||||
|
||||
const RECEIPT_PLAN_STATUS_MAP = {
|
||||
PENDING: { label: '待核销', type: 'warning' },
|
||||
EFFECTIVE: { label: '已核销', type: 'success' },
|
||||
CANCELLED: { label: '已作废', type: 'info' },
|
||||
REVERSED: { label: '已反核销', type: 'danger' }
|
||||
}
|
||||
function receiptPlanStatusLabel(s) {
|
||||
return RECEIPT_PLAN_STATUS_MAP[s]?.label || s || '-'
|
||||
}
|
||||
function receiptPlanStatusTag(s) {
|
||||
return RECEIPT_PLAN_STATUS_MAP[s]?.type || 'info'
|
||||
}
|
||||
|
||||
/** 打开账单选择器:只列与收款同币种的未核销账单,并回显已选 */
|
||||
function openBillPicker() {
|
||||
const customerId = route.query.customerId
|
||||
if (!customerId) {
|
||||
proxy.$modal.msgError('缺少客户信息,请重新进入页面')
|
||||
return
|
||||
}
|
||||
billPicker.visible = true
|
||||
billPicker.loading = true
|
||||
billPicker.rows = []
|
||||
billPicker.selected = []
|
||||
listUnsettledBill(customerId).then(res => {
|
||||
// 现阶段只核销人民币账单,与收款币种无关;核销上限为收款折算 CNY
|
||||
billPicker.rows = (res.data || []).filter(r => (r.currency || 'CNY') === 'CNY')
|
||||
nextTick(() => {
|
||||
billPickerTableRef.value?.clearSelection()
|
||||
const chosen = receiptDialog.form.writeoffPlans || []
|
||||
billPicker.rows.forEach(r => {
|
||||
if (chosen.some(p => p.arBillId === r.arBillId)) {
|
||||
billPickerTableRef.value?.toggleRowSelection(r, true)
|
||||
}
|
||||
})
|
||||
})
|
||||
}).finally(() => {
|
||||
billPicker.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
function onPickerSelectionChange(rows) {
|
||||
billPicker.selected = rows || []
|
||||
}
|
||||
|
||||
/** 确认选择:新增行默认核销额=min(账单未核销额CNY, 收款折算CNY剩余可用),保留已填金额 */
|
||||
function confirmBillPicker() {
|
||||
const oldMap = new Map((receiptDialog.form.writeoffPlans || []).map(p => [p.arBillId, p]))
|
||||
receiptDialog.form.writeoffPlans = billPicker.selected.map(r => {
|
||||
const existed = oldMap.get(r.arBillId)
|
||||
const unwritten = Number(r.unwrittenAmount) || 0
|
||||
const usedByOthers = receiptDialog.form.writeoffPlans
|
||||
.filter(p => p.arBillId !== r.arBillId)
|
||||
.reduce((s, p) => s + (Number(p.writeoffAmount) || 0), 0)
|
||||
const remainCny = Math.max(receiptBaseCny.value - usedByOthers, 0)
|
||||
return {
|
||||
arBillId: r.arBillId,
|
||||
billNo: r.billNo,
|
||||
billName: r.billName,
|
||||
currency: 'CNY',
|
||||
unwrittenAmount: unwritten,
|
||||
writeoffAmount: existed ? Number(existed.writeoffAmount) : Math.round(Math.min(unwritten, remainCny) * 100) / 100
|
||||
}
|
||||
})
|
||||
billPicker.visible = false
|
||||
}
|
||||
|
||||
function removeReceiptBill(index) {
|
||||
receiptDialog.form.writeoffPlans.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 提交前校验核销账单(金额口径均为 CNY) */
|
||||
function validateReceiptWriteoffPlans() {
|
||||
const cap = receiptBaseCny.value
|
||||
const list = receiptDialog.form.writeoffPlans || []
|
||||
let total = 0
|
||||
for (const p of list) {
|
||||
const amt = Number(p.writeoffAmount)
|
||||
if (!amt || amt <= 0) {
|
||||
proxy.$modal.msgError(`账单【${p.billNo}】本次核销金额必须大于0`)
|
||||
return false
|
||||
}
|
||||
if (amt > Number(p.unwrittenAmount)) {
|
||||
proxy.$modal.msgError(`账单【${p.billNo}】核销金额超过其未核销金额`)
|
||||
return false
|
||||
}
|
||||
total += amt
|
||||
}
|
||||
if (Math.round(total * 100) > Math.round(cap * 100)) {
|
||||
proxy.$modal.msgError(`核销金额合计 ${total.toFixed(2)} CNY 超过收款折算本位币金额 ${cap.toFixed(2)} CNY`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function submitReceipt() {
|
||||
receiptFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
@@ -1511,6 +1757,7 @@ function submitReceipt() {
|
||||
proxy.$modal.msgError(`请录入 ${cur} 对 CNY 的汇率(汇率表未配置时可手工填写)`)
|
||||
return
|
||||
}
|
||||
if (!validateReceiptWriteoffPlans()) return
|
||||
submitLoading.value = true
|
||||
addReceipt(receiptDialog.form).then(() => {
|
||||
proxy.$modal.msgSuccess("登记成功,等待审核")
|
||||
@@ -2222,6 +2469,17 @@ watch(() => route.query.customerId, (newId, oldId) => {
|
||||
.writeoff-summary {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.receipt-wo-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.receipt-wo-summary {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.wo-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user