Merge branch 'test' of http://git.lionquexpress.com/wuweihua/BMS into test
This commit is contained in:
@@ -47,7 +47,7 @@
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT" link type="success" v-hasPermi="['ar:bill:confirm']" @click="confirmBill(row)">确认</el-button>
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT || row.status === AR_BILL_STATUS.CONFIRMED" link type="warning" v-hasPermi="['ar:bill:void']" @click="voidBill(row)">作废</el-button>
|
||||
<el-button v-if="row.status !== AR_BILL_STATUS.DRAFT && row.status !== AR_BILL_STATUS.VOID" link type="primary" v-hasPermi="['ar:bill:export']" @click="exportBill(row)">导出明细</el-button>
|
||||
<el-button v-if="canGoWriteoff(row)" link type="primary" v-hasPermi="['customer:bill:writeoff']" @click="goWriteoff(row)">去核销</el-button>
|
||||
<el-button v-if="canGoWriteoff(row)" link type="primary" v-hasPermi="['customer:bill:writeoff']" @click="goWriteoff(row)">核销</el-button>
|
||||
<el-button v-if="canGoReverseWriteoff(row)" link type="warning" v-hasPermi="['customer:bill:writeoff:cancel']" @click="goReverseWriteoff(row)">反核销</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<el-table-column label="收款类型" align="center" prop="receiptType" width="95">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiptTypeTag(scope.row.receiptType)" size="small">
|
||||
{{ formatOption(receiptTypeOptions, scope.row.receiptType) }}
|
||||
{{ formatReceiptType(scope.row.receiptType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -295,7 +295,6 @@
|
||||
v-model="receiptDialog.form.customerId"
|
||||
placeholder="请选择客户(必选)"
|
||||
:page-size="1000"
|
||||
@change="handleReceiptCustomerChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -382,51 +381,6 @@
|
||||
<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>
|
||||
</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>核销;所选账单在收款单<strong>审核通过</strong>后一并核销,
|
||||
核销合计不能超过收款金额按汇率折算的本位币金额({{ formatMoney(receiptBaseCny) }} CNY);
|
||||
<strong>核销不扣减客户余额</strong>,正常计入客户余额。
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="收款凭证">
|
||||
<el-upload
|
||||
@@ -468,49 +422,6 @@
|
||||
</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="viewDialog.visible" width="720px" append-to-body>
|
||||
<el-descriptions :column="2" border v-if="viewDialog.data" size="small">
|
||||
@@ -521,7 +432,7 @@
|
||||
<el-descriptions-item label="客户">{{ viewDialog.data.customerName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款时间">{{ formatDateTime(viewDialog.data.receivedAt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款类型">
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatOption(receiptTypeOptions, viewDialog.data.receiptType) }}</el-tag>
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatReceiptType(viewDialog.data.receiptType) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="原币金额">
|
||||
<span class="money-in">{{ viewDialog.data.currency }} {{ formatMoney(viewDialog.data.amount) }}</span>
|
||||
@@ -676,7 +587,7 @@ import axios from 'axios'
|
||||
import { saveAs } from 'file-saver'
|
||||
import {
|
||||
listReceipt, getReceipt, addReceipt, confirmReceipt, cancelReceipt,
|
||||
listUnsettledBill, listReceiptWriteoff, receiptWriteoff, cancelReceiptWriteoff
|
||||
listReceiptWriteoff, receiptWriteoff, cancelReceiptWriteoff
|
||||
} from '@/api/ar/receipt'
|
||||
import { listArBill } from '@/api/ar/bill'
|
||||
import { convertToBase } from '@/api/data/exchangeRate'
|
||||
@@ -695,8 +606,7 @@ const activeTab = ref('receipt')
|
||||
/* ================= 枚举与格式化 ================= */
|
||||
const receiptTypeOptions = [
|
||||
{ value: 'PREPAY', label: '预充值' },
|
||||
{ value: 'REPAY', label: '还款核销' },
|
||||
{ value: 'MIXED', label: '混合' }
|
||||
{ value: 'REPAY', label: '还款' }
|
||||
]
|
||||
const receiptStatusOptions = [
|
||||
{ value: 'PENDING', label: '待确认' },
|
||||
@@ -724,8 +634,11 @@ function formatOption(options, value) {
|
||||
const opt = options.find(o => String(o.value) === String(value))
|
||||
return opt ? opt.label : value
|
||||
}
|
||||
function formatReceiptType(value) {
|
||||
return value === 'MIXED' ? '-' : formatOption(receiptTypeOptions, value)
|
||||
}
|
||||
function receiptTypeTag(v) {
|
||||
return { PREPAY: '', REPAY: 'warning', MIXED: 'info' }[v] || 'info'
|
||||
return { PREPAY: '', REPAY: 'warning' }[v] || 'info'
|
||||
}
|
||||
function receiptStatusTag(v) {
|
||||
return { PENDING: 'warning', CONFIRMED: 'success', CANCELLED: 'danger' }[v] || 'info'
|
||||
@@ -860,15 +773,11 @@ function resetReceiptQuery() {
|
||||
|
||||
/** 审核通过 */
|
||||
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()
|
||||
if (writeoffLoaded) {
|
||||
getBillList()
|
||||
getWriteoffList()
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
@@ -908,8 +817,7 @@ function resetReceiptForm() {
|
||||
bankAccountNo: '',
|
||||
bankTransactionNo: '',
|
||||
remark: '',
|
||||
attachmentList: [],
|
||||
writeoffPlans: []
|
||||
attachmentList: []
|
||||
}
|
||||
proxy.resetForm('receiptFormRef')
|
||||
}
|
||||
@@ -919,14 +827,6 @@ function handleAddReceipt() {
|
||||
receiptDialog.visible = true
|
||||
}
|
||||
|
||||
/** 切换客户:核销账单归属于客户,已选账单需清空重选 */
|
||||
function handleReceiptCustomerChange() {
|
||||
const plans = receiptDialog.form.writeoffPlans || []
|
||||
if (plans.length) {
|
||||
plans.splice(0, plans.length)
|
||||
}
|
||||
}
|
||||
|
||||
const rateEdited = ref(false)
|
||||
const rateSourceInfo = ref({ source: '', ratePath: '' })
|
||||
|
||||
@@ -978,22 +878,6 @@ watch(() => receiptDialog.form.receivedAt, () => {
|
||||
}
|
||||
})
|
||||
|
||||
/* ============ 选择核销账单 ============ */
|
||||
const billPickerTableRef = ref(null)
|
||||
const billPicker = reactive({ visible: false, loading: false, rows: [], selected: [] })
|
||||
|
||||
const receiptBaseCny = computed(() => {
|
||||
const amt = Number(receiptDialog.form.amount) || 0
|
||||
const rate = Number(receiptDialog.form.exchangeRate) || 0
|
||||
return Math.round(amt * rate * 100) / 100
|
||||
})
|
||||
const receiptWriteoffTotal = computed(() => {
|
||||
return (receiptDialog.form.writeoffPlans || []).reduce((s, r) => s + (Number(r.writeoffAmount) || 0), 0)
|
||||
})
|
||||
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' },
|
||||
@@ -1007,84 +891,6 @@ function receiptPlanStatusTag(s) {
|
||||
return RECEIPT_PLAN_STATUS_MAP[s]?.type || 'info'
|
||||
}
|
||||
|
||||
function openBillPicker() {
|
||||
const customerId = receiptDialog.form.customerId
|
||||
if (!customerId) {
|
||||
proxy.$modal.msgError('请先选择客户')
|
||||
return
|
||||
}
|
||||
billPicker.visible = true
|
||||
billPicker.loading = true
|
||||
billPicker.rows = []
|
||||
billPicker.selected = []
|
||||
listUnsettledBill(customerId).then(res => {
|
||||
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 || []
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1094,7 +900,6 @@ function submitReceipt() {
|
||||
proxy.$modal.msgError(`请录入 ${cur} 对 CNY 的汇率(汇率表未配置时可手工填写)`)
|
||||
return
|
||||
}
|
||||
if (!validateReceiptWriteoffPlans()) return
|
||||
submitLoading.value = true
|
||||
addReceipt(receiptDialog.form).then(() => {
|
||||
proxy.$modal.msgSuccess('登记成功,等待审核')
|
||||
@@ -1393,12 +1198,6 @@ getReceiptList()
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.receipt-wo-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.attach-title {
|
||||
font-weight: 600;
|
||||
margin: 12px 0 8px;
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
<el-tab-pane label="绑定产品" name="product">
|
||||
<div class="pane-toolbar">
|
||||
<product-select v-model="form.productCodes" />
|
||||
<span class="pane-tip">绑定结果以产品代码逗号串存入客户主表 product_codes,产品代码在新旧库间稳定,用于产品对接映射</span>
|
||||
<!-- <span class="pane-tip">绑定结果以产品代码逗号串存入客户主表 product_codes,产品代码在新旧库间稳定,用于产品对接映射</span>-->
|
||||
</div>
|
||||
<el-table :data="boundProducts" border size="small" class="bound-table">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
@@ -216,7 +216,7 @@
|
||||
<el-button type="primary" plain icon="Plus" size="small" :disabled="isAdd" @click="openPriceSetDialog()">
|
||||
{{ boundPriceSets.length ? '更换套餐' : '绑定套餐' }}
|
||||
</el-button>
|
||||
<span class="pane-tip">绑定后写入客户套餐关系表 prc_customer_price_set,一个客户仅可绑定一个价格套餐,仅「生效中」的套餐可绑定</span>
|
||||
<span class="pane-tip">一个客户仅可绑定一个价格套餐,仅「生效中」的套餐可绑定</span>
|
||||
</div>
|
||||
<el-table :data="boundPriceSets" border size="small" class="bound-table">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
@@ -258,7 +258,7 @@
|
||||
<el-tab-pane label="公司主体" name="company">
|
||||
<div class="pane-toolbar">
|
||||
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddCompany">新增公司主体</el-button>
|
||||
<span class="pane-tip">点击「绑定客户」后,主表的统一社会信用代码会自动同步为该公司的信用代码</span>
|
||||
<!-- <span class="pane-tip">点击「绑定客户」后,主表的统一社会信用代码会自动同步为该公司的信用代码</span>-->
|
||||
</div>
|
||||
<el-table :data="form.companyList" border size="small">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
|
||||
+15
-250
@@ -127,7 +127,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="收款类型" align="center" prop="receiptType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiptTypeTag(scope.row.receiptType)">{{ formatOption(receiptTypeOptions, scope.row.receiptType) }}</el-tag>
|
||||
<el-tag :type="receiptTypeTag(scope.row.receiptType)">{{ formatReceiptType(scope.row.receiptType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="原币金额" align="center" width="130">
|
||||
@@ -679,54 +679,6 @@
|
||||
<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
|
||||
@@ -778,7 +730,7 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收款日期">{{ formatDateTime(viewDialog.data.receivedAt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款类型">
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatOption(receiptTypeOptions, viewDialog.data.receiptType) }}</el-tag>
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatReceiptType(viewDialog.data.receiptType) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="原币金额">
|
||||
<span class="money-in">{{ viewDialog.data.currency }} {{ formatMoney(viewDialog.data.amount) }}</span>
|
||||
@@ -997,60 +949,14 @@
|
||||
</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-dialog title="账单核销" v-model="woDialog.visible" width="560px" append-to-body>
|
||||
<el-descriptions :column="1" border size="small" v-if="woDialog.bill">
|
||||
<el-descriptions-item label="账单号">{{ woDialog.bill.billNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单名称">{{ woDialog.bill.billName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单未核销金额">
|
||||
<span class="money-out">CNY {{ formatMoney(woDialog.bill.unwrittenAmount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="账户可用余额">
|
||||
<span class="money-in">CNY {{ formatMoney(currentAccount.balance) }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form :model="woDialog.form" label-width="110px" class="wo-form">
|
||||
<el-form-item label="本次核销金额" required>
|
||||
@@ -1142,8 +1048,7 @@ const customer = ref({})
|
||||
/* ================= 枚举 ================= */
|
||||
const receiptTypeOptions = [
|
||||
{ value: 'PREPAY', label: '预充值' },
|
||||
{ value: 'REPAY', label: '还款核销' },
|
||||
{ value: 'MIXED', label: '混合' }
|
||||
{ value: 'REPAY', label: '还款' }
|
||||
]
|
||||
const receiptStatusOptions = [
|
||||
{ value: 'PENDING', label: '待确认' },
|
||||
@@ -1184,8 +1089,11 @@ function formatOption(options, value) {
|
||||
const opt = options.find(o => String(o.value) === String(value))
|
||||
return opt ? opt.label : value
|
||||
}
|
||||
function formatReceiptType(value) {
|
||||
return value === 'MIXED' ? '-' : formatOption(receiptTypeOptions, value)
|
||||
}
|
||||
function receiptTypeTag(v) {
|
||||
return { PREPAY: '', REPAY: 'warning', MIXED: 'info' }[v] || 'info'
|
||||
return { PREPAY: '', REPAY: 'warning' }[v] || 'info'
|
||||
}
|
||||
function receiptStatusTag(v) {
|
||||
return { PENDING: 'warning', CONFIRMED: 'success', CANCELLED: 'danger' }[v] || 'info'
|
||||
@@ -1517,10 +1425,10 @@ 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()
|
||||
@@ -1566,8 +1474,7 @@ function resetReceiptForm() {
|
||||
bankAccountNo: '',
|
||||
bankTransactionNo: '',
|
||||
remark: '',
|
||||
attachmentList: [],
|
||||
writeoffPlans: []
|
||||
attachmentList: []
|
||||
}
|
||||
proxy.resetForm('receiptFormRef')
|
||||
}
|
||||
@@ -1609,7 +1516,7 @@ function fetchDefaultRate() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 切换币种:重置手工标记并重新取默认汇率;核销账单固定为CNY,只需按新汇率重算可核销上限,不清空已选 */
|
||||
/** 切换币种:重置手工标记并重新取默认汇率 */
|
||||
function handleCurrencyChange() {
|
||||
rateEdited.value = false
|
||||
rateSourceInfo.value = { source: '', ratePath: '' }
|
||||
@@ -1635,35 +1542,6 @@ 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' },
|
||||
@@ -1677,88 +1555,6 @@ 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
|
||||
@@ -1768,7 +1564,6 @@ function submitReceipt() {
|
||||
proxy.$modal.msgError(`请录入 ${cur} 对 CNY 的汇率(汇率表未配置时可手工填写)`)
|
||||
return
|
||||
}
|
||||
if (!validateReceiptWriteoffPlans()) return
|
||||
submitLoading.value = true
|
||||
addReceipt(receiptDialog.form).then(() => {
|
||||
proxy.$modal.msgSuccess("登记成功,等待审核")
|
||||
@@ -2106,10 +1901,9 @@ function loadBillWriteoffData() {
|
||||
|
||||
/* ---- 核销弹窗 ---- */
|
||||
const woSubmitLoading = ref(false)
|
||||
const woDialog = reactive({ visible: false, bill: null, form: {}, accountLoading: false })
|
||||
let writeoffAccountRequestSerial = 0
|
||||
const woDialog = reactive({ visible: false, bill: null, form: {} })
|
||||
|
||||
async function openWriteoffDialog(row) {
|
||||
function openWriteoffDialog(row) {
|
||||
const customerId = Number(route.query.customerId)
|
||||
if (!customerId) {
|
||||
proxy.$modal.msgError('缺少客户ID,无法发起核销')
|
||||
@@ -2127,27 +1921,9 @@ async function openWriteoffDialog(row) {
|
||||
remark: ''
|
||||
}
|
||||
woDialog.visible = true
|
||||
|
||||
// 仍刷新账户余额供展示,但不再限制核销金额
|
||||
const requestSerial = ++writeoffAccountRequestSerial
|
||||
woDialog.accountLoading = true
|
||||
try {
|
||||
const res = await listAccount(customerId)
|
||||
if (requestSerial !== writeoffAccountRequestSerial) return
|
||||
accountList.value = res.data || []
|
||||
} catch (e) {
|
||||
if (requestSerial !== writeoffAccountRequestSerial) return
|
||||
proxy.$modal.msgError('账户余额刷新失败,请稍后重试')
|
||||
} finally {
|
||||
if (requestSerial === writeoffAccountRequestSerial) woDialog.accountLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
function submitWriteoff() {
|
||||
if (woDialog.accountLoading) {
|
||||
proxy.$modal.msgWarning('正在刷新账户余额,请稍候')
|
||||
return
|
||||
}
|
||||
const bill = woDialog.bill
|
||||
const rawAmount = typeof woDialog.form.writeoffAmount === 'string'
|
||||
? woDialog.form.writeoffAmount.trim()
|
||||
@@ -2487,17 +2263,6 @@ 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;
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
<el-tab-pane label="绑定渠道" name="channel">
|
||||
<div class="pane-toolbar">
|
||||
<channel-select v-model="form.channelIds" />
|
||||
<span class="pane-tip">绑定结果以渠道编码逗号串存入供应商主表 channel_ids,渠道来源于老系统服务渠道镜像(每日自动同步)</span>
|
||||
<!-- <span class="pane-tip">绑定结果以渠道编码逗号串存入供应商主表 channel_ids,渠道来源于老系统服务渠道镜像(每日自动同步)</span>-->
|
||||
</div>
|
||||
<el-table :data="boundChannels" border size="small" class="bound-table">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
@@ -217,7 +217,7 @@
|
||||
<el-tab-pane label="公司主体" name="company">
|
||||
<div class="pane-toolbar">
|
||||
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddCompany">新增公司主体</el-button>
|
||||
<span class="pane-tip">点击「绑定供应商」后,主表的统一社会信用代码会自动同步为该公司的信用代码</span>
|
||||
<!-- <span class="pane-tip">点击「绑定供应商」后,主表的统一社会信用代码会自动同步为该公司的信用代码</span>-->
|
||||
</div>
|
||||
<el-table :data="form.companyList" border size="small">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
|
||||
Reference in New Issue
Block a user