feat(bx): 报销支付按未结清余额分次冲抵和发放。
冲抵后的余额不再自动记成发放,发放和冲抵分开提交,未结清单据继续留在待处理。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
b49c99f1bd
commit
e7f942b399
8 files changed
+707
-261
No files matched your search
@@ -252,6 +252,17 @@
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="未结清余额(元)"
|
||||
min-width="130"
|
||||
align="right"
|
||||
header-align="center"
|
||||
class-name="col-money"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatSumMoney(unpaidBalance(row)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ListTable>
|
||||
</div>
|
||||
<div ref="pendingPaymentBelowRef">
|
||||
@@ -564,6 +575,17 @@
|
||||
{{ row.payTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="未结清余额(元)"
|
||||
min-width="130"
|
||||
align="right"
|
||||
header-align="center"
|
||||
class-name="col-money"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatSumMoney(unpaidBalance(row)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ListTable>
|
||||
</div>
|
||||
<div ref="processedPaymentBelowRef">
|
||||
@@ -639,51 +661,6 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 标记已发放 -->
|
||||
<el-dialog
|
||||
v-model="dialogs.issue"
|
||||
title="标记已发放"
|
||||
width="640px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form label-position="top" class="bx-dialog-form">
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="发放金额(元)" class="field-amount">
|
||||
<el-input :model-value="String(currentRow.amount || '')" disabled placeholder="发放金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="实际发放金额(元)" class="field-amount">
|
||||
<el-input v-model="actualAmount" placeholder="请输入实际发放金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="grid-column: 1 / -1">
|
||||
<el-input v-model="issueRemark" type="textarea" :rows="4" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="form-section form-section-last">
|
||||
<h4>发放附件</h4>
|
||||
<BxAttachmentUpload
|
||||
ref="issueAttachmentRef"
|
||||
v-model="uploadedFiles"
|
||||
biz-type="reimburse_pay"
|
||||
:biz-id="currentRow.id"
|
||||
hint="支持上传发放截图等凭证。"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="dialogs.issue = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="loadingMap.issueSubmit" @click="submitIssue">
|
||||
<el-icon><Check /></el-icon>确认发放
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 批量发放 -->
|
||||
<el-dialog
|
||||
v-model="dialogs.batchIssue"
|
||||
@@ -741,10 +718,10 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 冲抵备用金 -->
|
||||
<!-- 发放 / 冲抵,按入口只展示对应表单 -->
|
||||
<el-dialog
|
||||
v-model="dialogs.offset"
|
||||
title="冲抵备用金"
|
||||
:title="paymentActionTab === 'pay' ? '发放' : '冲抵备用金'"
|
||||
width="960px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
@@ -760,84 +737,127 @@
|
||||
<el-form-item label="报销金额(元)" class="field-amount">
|
||||
<el-input :model-value="String(currentRow.amount || '')" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="冲抵金额(元)" class="field-amount">
|
||||
<el-input v-model="offsetAmount" placeholder="请输入冲抵金额" />
|
||||
<el-form-item label="已冲抵金额(元)" class="field-amount">
|
||||
<el-input :model-value="formatMoneyYuan(currentRow.offsetAmount || 0)" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="发放金额(元)" class="field-amount">
|
||||
<el-input v-model="offsetPayAmount" placeholder="冲抵后剩余直接发放" />
|
||||
<el-form-item label="已发放金额(元)" class="field-amount">
|
||||
<el-input :model-value="formatMoneyYuan(currentRow.actualAmount || 0)" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="未结清余额(元)" class="field-amount">
|
||||
<el-input :model-value="formatMoneyYuan(unpaidBalance(currentRow))" disabled />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<h4 class="dialog-section-title">已发放备用金列表</h4>
|
||||
<div class="inline-row">
|
||||
<el-input v-model="offsetAdvanceFilter.keyword" placeholder="搜索单号/申请人" class="inline-grow" clearable />
|
||||
<el-button class="btn-main" type="primary" :loading="offsetAdvanceLoading" @click="handleOffsetAdvanceSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
<el-button class="btn-ghost" @click="handleOffsetAdvanceReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
</div>
|
||||
<ListTable table-key="bx-payment-3"
|
||||
class="list-table"
|
||||
:data="offsetAdvances"
|
||||
v-loading="offsetAdvanceLoading"
|
||||
max-height="300"
|
||||
stripe
|
||||
@selection-change="handleOffsetSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" header-align="center" />
|
||||
<el-table-column type="index" label="序号" width="68" align="center" header-align="center" />
|
||||
<el-table-column prop="applyCode" label="申请单号" min-width="130" align="center" header-align="center" />
|
||||
<el-table-column prop="applicantName" label="申请人" min-width="80" align="center" header-align="center" />
|
||||
<el-table-column prop="reason" label="申请原因" min-width="120" align="left" header-align="center" show-overflow-tooltip />
|
||||
<el-table-column label="已发放金额(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(row.actualAmount || row.amount || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已冲抵金额(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(row.offsetAmount || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剩余可冲抵(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(Number(row.actualAmount || row.amount || 0) - Number(row.offsetAmount || 0)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ListTable>
|
||||
<div class="offset-pager">
|
||||
<el-pagination
|
||||
v-model:current-page="offsetAdvancePage"
|
||||
v-model:page-size="offsetAdvancePageSize"
|
||||
:total="offsetAdvanceTotal"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:page-sizes="[10, 20, 100, 500]"
|
||||
@current-change="fetchOffsetAdvances"
|
||||
@size-change="handleOffsetAdvanceSizeChange"
|
||||
<template v-if="paymentActionTab === 'offset'">
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="本次冲抵金额(元)" class="field-amount">
|
||||
<el-input v-model="offsetAmount" placeholder="请输入本次冲抵金额" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<h4 class="dialog-section-title">已发放备用金列表</h4>
|
||||
<div class="inline-row">
|
||||
<el-input v-model="offsetAdvanceFilter.keyword" placeholder="搜索单号/申请人" class="inline-grow" clearable />
|
||||
<el-button class="btn-main" type="primary" :loading="offsetAdvanceLoading" @click="handleOffsetAdvanceSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
<el-button class="btn-ghost" @click="handleOffsetAdvanceReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
</div>
|
||||
<ListTable table-key="bx-payment-3"
|
||||
class="list-table"
|
||||
:data="offsetAdvances"
|
||||
v-loading="offsetAdvanceLoading"
|
||||
max-height="300"
|
||||
stripe
|
||||
@selection-change="handleOffsetSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" header-align="center" />
|
||||
<el-table-column type="index" label="序号" width="68" align="center" header-align="center" />
|
||||
<el-table-column prop="applyCode" label="申请单号" min-width="130" align="center" header-align="center" />
|
||||
<el-table-column prop="applicantName" label="申请人" min-width="80" align="center" header-align="center" />
|
||||
<el-table-column prop="reason" label="申请原因" min-width="120" align="left" header-align="center" show-overflow-tooltip />
|
||||
<el-table-column label="已发放金额(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(row.actualAmount || row.amount || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已冲抵金额(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(row.offsetAmount || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剩余可冲抵(元)" min-width="100" class-name="col-money" align="right" header-align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(Number(row.actualAmount || row.amount || 0) - Number(row.offsetAmount || 0)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ListTable>
|
||||
<div class="offset-pager">
|
||||
<el-pagination
|
||||
v-model:current-page="offsetAdvancePage"
|
||||
v-model:page-size="offsetAdvancePageSize"
|
||||
:total="offsetAdvanceTotal"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:page-sizes="[10, 20, 100, 500]"
|
||||
@current-change="fetchOffsetAdvances"
|
||||
@size-change="handleOffsetAdvanceSizeChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="offsetRemark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
<h4 class="dialog-section-title">冲抵附件(必填)</h4>
|
||||
<BxAttachmentUpload
|
||||
ref="offsetAttachmentRef"
|
||||
v-model="uploadedFiles"
|
||||
biz-type="reimburse_offset"
|
||||
:biz-id="currentRow.id"
|
||||
hint="请至少上传 1 个冲抵附件。"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="offsetRemark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
<h4 class="dialog-section-title">冲抵附件(必填)</h4>
|
||||
<BxAttachmentUpload
|
||||
ref="offsetAttachmentRef"
|
||||
v-model="uploadedFiles"
|
||||
biz-type="reimburse_offset"
|
||||
:biz-id="currentRow.id"
|
||||
hint="请至少上传 1 个冲抵附件。"
|
||||
/>
|
||||
<template v-else>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="本次发放金额(元)" class="field-amount">
|
||||
<el-input v-model="actualAmount" placeholder="请输入本次发放金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发放备注" style="grid-column: 1 / -1">
|
||||
<el-input v-model="issueRemark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<h4 class="dialog-section-title">发放附件</h4>
|
||||
<BxAttachmentUpload
|
||||
ref="issueAttachmentRef"
|
||||
v-model="payFiles"
|
||||
biz-type="reimburse_pay"
|
||||
:biz-id="currentRow.id"
|
||||
hint="支持上传本次发放截图等凭证。"
|
||||
/>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="dialogs.offset = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="loadingMap.offsetSubmit" @click="submitOffset">
|
||||
<el-button
|
||||
v-if="paymentActionTab === 'pay'"
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
:loading="loadingMap.issueSubmit"
|
||||
@click="submitIssue"
|
||||
>
|
||||
<el-icon><Check /></el-icon>确认发放
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
:loading="loadingMap.offsetSubmit"
|
||||
@click="submitOffset"
|
||||
>
|
||||
<el-icon><Check /></el-icon>确认冲抵
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -961,7 +981,7 @@ import { bxViewKv, bxViewKvMoney } from '@/utils/bx/view-kv'
|
||||
import type { StatsRowColumn } from '@/utils/list-column-stats'
|
||||
import { paymentColumnFilterDefs } from './columnFilterDefs'
|
||||
|
||||
type DialogKey = 'viewDetail' | 'issue' | 'batchIssue' | 'offset' | 'processedDetail'
|
||||
type DialogKey = 'viewDetail' | 'batchIssue' | 'offset' | 'processedDetail'
|
||||
|
||||
const listColumnDefs = [
|
||||
{ prop: 'applyTime', label: '申请时间' },
|
||||
@@ -1030,9 +1050,16 @@ function buildPaymentStatsColumns(opts: {
|
||||
money: PAYMENT_MONEY_PROPS.has(def.prop)
|
||||
})
|
||||
}
|
||||
cols.push({ key: 'unpaidBalance', interactive: false, prop: 'unpaidBalance', money: true })
|
||||
return cols
|
||||
}
|
||||
|
||||
function unpaidBalance(row: any) {
|
||||
const value =
|
||||
Number(row?.amount || 0) - Number(row?.offsetAmount || 0) - Number(row?.actualAmount || 0)
|
||||
return Math.max(0, Math.round(value * 100) / 100)
|
||||
}
|
||||
|
||||
const pendingPaymentStatsRowColumns = computed(() =>
|
||||
buildPaymentStatsColumns({ withSelection: true, mode: 'pending' })
|
||||
)
|
||||
@@ -1042,6 +1069,7 @@ const processedPaymentStatsRowColumns = computed(() =>
|
||||
|
||||
function getPaymentStatCellValue(row: any, prop: string): unknown {
|
||||
if (prop === 'payAmount') return row?.actualAmount
|
||||
if (prop === 'unpaidBalance') return unpaidBalance(row)
|
||||
if (prop === 'processStatus') return processedStatusLabel(row?.status, row?.payStatus)
|
||||
return row?.[prop]
|
||||
}
|
||||
@@ -1049,7 +1077,6 @@ function getPaymentStatCellValue(row: any, prop: string): unknown {
|
||||
const loadingMap = reactive<Record<string, boolean>>({})
|
||||
const dialogs = reactive<Record<DialogKey, boolean>>({
|
||||
viewDetail: false,
|
||||
issue: false,
|
||||
batchIssue: false,
|
||||
offset: false,
|
||||
processedDetail: false
|
||||
@@ -1172,15 +1199,10 @@ const viewBasicKvFields = computed<ViewField[]>(() => {
|
||||
|
||||
const viewPayKvFields = computed<ViewField[]>(() => {
|
||||
const d = viewData.value
|
||||
const payAmount = Number(d.amount || 0) - Number(d.offsetAmount || 0)
|
||||
return [
|
||||
bxViewKvMoney('冲抵金额(元)', formatMoneyYuan(d.offsetAmount || 0), { long: false }),
|
||||
bxViewKvMoney('发放金额(元)', formatMoneyYuan(Math.max(0, payAmount)), { long: false }),
|
||||
bxViewKvMoney(
|
||||
'实际发放金额(元)',
|
||||
d.actualAmount != null && d.actualAmount !== '' ? formatMoneyYuan(d.actualAmount) : '—',
|
||||
{ long: false }
|
||||
),
|
||||
bxViewKvMoney('已冲抵金额(元)', formatMoneyYuan(d.offsetAmount || 0), { long: false }),
|
||||
bxViewKvMoney('已发放金额(元)', formatMoneyYuan(d.actualAmount || 0), { long: false }),
|
||||
bxViewKvMoney('未结清余额(元)', formatMoneyYuan(unpaidBalance(d)), { long: false }),
|
||||
bxViewKv('发放时间', d.payTime, { long: false }),
|
||||
bxViewKv('发放操作人', d.payUserName, { long: false }),
|
||||
bxViewKv('发放备注', d.payRemark, { long: false })
|
||||
@@ -1203,6 +1225,7 @@ const batchLines = ref<
|
||||
>([])
|
||||
const offsetRemark = ref('')
|
||||
const uploadedFiles = ref<BxAttachmentItem[]>([])
|
||||
const payFiles = ref<BxAttachmentItem[]>([])
|
||||
const issueAttachmentRef = ref<InstanceType<typeof BxAttachmentUpload> | null>(null)
|
||||
const offsetAttachmentRef = ref<InstanceType<typeof BxAttachmentUpload> | null>(null)
|
||||
const viewAttachments = ref<BxAttachmentItem[]>([])
|
||||
@@ -1215,18 +1238,7 @@ const offsetAdvancePageSize = ref(5)
|
||||
const offsetAdvanceLoading = ref(false)
|
||||
const offsetSelectedAdvances = ref<any[]>([])
|
||||
const offsetAmount = ref('')
|
||||
const offsetPayAmount = computed({
|
||||
get: () => {
|
||||
const amt = Number(currentRow.value.amount || 0)
|
||||
const offset = Number(offsetAmount.value) || 0
|
||||
return String(Math.max(0, amt - offset))
|
||||
},
|
||||
set: (val: string) => {
|
||||
const amt = Number(currentRow.value.amount || 0)
|
||||
const pay = Number(val) || 0
|
||||
offsetAmount.value = String(Math.max(0, amt - pay))
|
||||
}
|
||||
})
|
||||
const paymentActionTab = ref<'offset' | 'pay'>('offset')
|
||||
|
||||
|
||||
function getPaymentPendingExportFilterParams() {
|
||||
@@ -1284,15 +1296,10 @@ const processedBasicKvFields = computed<ViewField[]>(() => {
|
||||
|
||||
const processedPayKvFields = computed<ViewField[]>(() => {
|
||||
const d = processedViewData.value
|
||||
const payAmount = Number(d.amount || 0) - Number(d.offsetAmount || 0)
|
||||
return [
|
||||
bxViewKvMoney('冲抵金额(元)', formatMoneyYuan(d.offsetAmount || 0), { long: false }),
|
||||
bxViewKvMoney('发放金额(元)', formatMoneyYuan(Math.max(0, payAmount)), { long: false }),
|
||||
bxViewKvMoney(
|
||||
'实际发放金额(元)',
|
||||
formatMoneyYuan(d.actualAmount ?? Math.max(0, payAmount)),
|
||||
{ long: false }
|
||||
),
|
||||
bxViewKvMoney('已冲抵金额(元)', formatMoneyYuan(d.offsetAmount || 0), { long: false }),
|
||||
bxViewKvMoney('已发放金额(元)', formatMoneyYuan(d.actualAmount || 0), { long: false }),
|
||||
bxViewKvMoney('未结清余额(元)', formatMoneyYuan(unpaidBalance(d)), { long: false }),
|
||||
bxViewKv('发放时间', d.payTime, { long: false }),
|
||||
bxViewKv('发放操作人', d.payUserName, { long: false }),
|
||||
bxViewKv('发放备注', d.payRemark, { long: false })
|
||||
@@ -1322,20 +1329,28 @@ function onSelectionChange(rows: any[]) {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
function handleToolbarIssue() {
|
||||
function requireSinglePendingRow() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先勾选一条待处理记录')
|
||||
return
|
||||
return null
|
||||
}
|
||||
openIssueDialog(selectedRows.value[0])
|
||||
if (selectedRows.value.length > 1) {
|
||||
ElMessage.warning('只能选择一条待处理记录')
|
||||
return null
|
||||
}
|
||||
return selectedRows.value[0]
|
||||
}
|
||||
|
||||
function handleToolbarIssue() {
|
||||
const row = requireSinglePendingRow()
|
||||
if (!row) return
|
||||
openIssueDialog(row)
|
||||
}
|
||||
|
||||
function handleToolbarOffset() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先勾选一条待处理记录')
|
||||
return
|
||||
}
|
||||
openOffsetDialog(selectedRows.value[0])
|
||||
const row = requireSinglePendingRow()
|
||||
if (!row) return
|
||||
openOffsetDialog(row)
|
||||
}
|
||||
|
||||
function buildPendingQuery() {
|
||||
@@ -1411,11 +1426,7 @@ async function openView(row: any) {
|
||||
}
|
||||
|
||||
function openIssueDialog(row: any) {
|
||||
currentRow.value = row
|
||||
issueRemark.value = ''
|
||||
actualAmount.value = String(row.amount ?? '')
|
||||
uploadedFiles.value = []
|
||||
dialogs.issue = true
|
||||
openOffsetDialog(row, 'pay')
|
||||
}
|
||||
|
||||
function openBatchIssueDialog() {
|
||||
@@ -1424,14 +1435,13 @@ function openBatchIssueDialog() {
|
||||
return
|
||||
}
|
||||
batchLines.value = selectedRows.value.map((row) => {
|
||||
const payAmount = String(row.amount ?? '')
|
||||
return {
|
||||
id: String(row.id),
|
||||
applyCode: row.applyCode || '',
|
||||
applicantName: row.applicantName || '',
|
||||
title: row.title || '',
|
||||
payAmount,
|
||||
actualAmount: payAmount
|
||||
payAmount: String(row.amount ?? ''),
|
||||
actualAmount: String(unpaidBalance(row))
|
||||
}
|
||||
})
|
||||
batchIssueRemark.value = ''
|
||||
@@ -1469,20 +1479,41 @@ async function submitBatchIssue() {
|
||||
}
|
||||
}
|
||||
|
||||
async function keepOrCloseBill() {
|
||||
await refresh()
|
||||
const latest = rows.value.find((item) => item.id === currentRow.value.id)
|
||||
if (!latest || unpaidBalance(latest) <= 0) {
|
||||
dialogs.offset = false
|
||||
return
|
||||
}
|
||||
currentRow.value = { ...latest }
|
||||
}
|
||||
|
||||
async function submitIssue() {
|
||||
if (issueAttachmentRef.value?.hasUploading()) {
|
||||
ElMessage.warning('附件上传中,请稍候')
|
||||
return
|
||||
}
|
||||
const amount = Number(actualAmount.value)
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
ElMessage.warning('请填写本次发放金额')
|
||||
return
|
||||
}
|
||||
if (amount > unpaidBalance(currentRow.value) + 0.001) {
|
||||
ElMessage.warning('发放金额超过未结清余额')
|
||||
return
|
||||
}
|
||||
loadingMap.issueSubmit = true
|
||||
try {
|
||||
await paymentApi.pay(currentRow.value.id, {
|
||||
payRemark: issueRemark.value,
|
||||
actualAmount: Number(actualAmount.value) || undefined
|
||||
actualAmount: amount
|
||||
})
|
||||
ElMessage.success('发放成功')
|
||||
dialogs.issue = false
|
||||
await refresh()
|
||||
actualAmount.value = ''
|
||||
issueRemark.value = ''
|
||||
payFiles.value = []
|
||||
await keepOrCloseBill()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || '发放失败')
|
||||
} finally {
|
||||
@@ -1532,44 +1563,42 @@ function getAdvanceAvailable(adv: any): number {
|
||||
return Math.max(0, Number(adv.actualAmount || adv.amount || 0) - Number(adv.offsetAmount || 0))
|
||||
}
|
||||
|
||||
function calcAutoOffsetAmount(selected: any[], reimburseAmount: number): number {
|
||||
const totalAvailable = selected.reduce((sum, adv) => sum + getAdvanceAvailable(adv), 0)
|
||||
return Math.min(totalAvailable, reimburseAmount)
|
||||
}
|
||||
|
||||
function handleOffsetSelectionChange(rows: any[]) {
|
||||
offsetSelectedAdvances.value = rows
|
||||
const reimburseAmount = Number(currentRow.value.amount || 0)
|
||||
offsetAmount.value = String(calcAutoOffsetAmount(rows, reimburseAmount))
|
||||
}
|
||||
|
||||
function openOffsetDialog(row: any) {
|
||||
function openOffsetDialog(row: any, tab: 'offset' | 'pay' = 'offset') {
|
||||
currentRow.value = row
|
||||
paymentActionTab.value = tab
|
||||
offsetAdvanceFilter.keyword = ''
|
||||
offsetAdvancePage.value = 1
|
||||
offsetSelectedAdvances.value = []
|
||||
offsetAmount.value = '0'
|
||||
offsetAmount.value = ''
|
||||
actualAmount.value = ''
|
||||
issueRemark.value = ''
|
||||
offsetRemark.value = ''
|
||||
uploadedFiles.value = []
|
||||
payFiles.value = []
|
||||
dialogs.offset = true
|
||||
fetchOffsetAdvances()
|
||||
if (tab === 'offset') fetchOffsetAdvances()
|
||||
}
|
||||
|
||||
function buildOffsetItems(totalOffset: number) {
|
||||
const items: Array<{ advanceId: number; offsetAmount: number; applyCode: string }> = []
|
||||
const items: Array<{ advanceId: string; offsetAmount: number; applyCode: string }> = []
|
||||
let remaining = totalOffset
|
||||
for (const adv of offsetSelectedAdvances.value) {
|
||||
if (remaining <= 0) break
|
||||
if (remaining <= 0.001) break
|
||||
const available = getAdvanceAvailable(adv)
|
||||
if (available <= 0) continue
|
||||
const toOffset = Math.min(remaining, available)
|
||||
items.push({
|
||||
advanceId: adv.id,
|
||||
offsetAmount: toOffset,
|
||||
offsetAmount: Math.round(toOffset * 100) / 100,
|
||||
applyCode: adv.applyCode || String(adv.id)
|
||||
})
|
||||
remaining -= toOffset
|
||||
}
|
||||
return items
|
||||
return { items, leftover: remaining }
|
||||
}
|
||||
|
||||
async function submitOffset() {
|
||||
@@ -1582,6 +1611,10 @@ async function submitOffset() {
|
||||
ElMessage.warning('请填写冲抵金额')
|
||||
return
|
||||
}
|
||||
if (totalOffset > unpaidBalance(currentRow.value) + 0.001) {
|
||||
ElMessage.warning('冲抵金额超过未结清余额')
|
||||
return
|
||||
}
|
||||
if (uploadedFiles.value.length === 0) {
|
||||
ElMessage.warning('请至少上传 1 个冲抵附件')
|
||||
return
|
||||
@@ -1591,16 +1624,18 @@ async function submitOffset() {
|
||||
return
|
||||
}
|
||||
|
||||
const items = buildOffsetItems(totalOffset)
|
||||
const payAmount = Number(offsetPayAmount.value) || 0
|
||||
const { items, leftover } = buildOffsetItems(totalOffset)
|
||||
if (!items.length || leftover > 0.001) {
|
||||
ElMessage.warning('冲抵金额超过所选备用金剩余可冲抵')
|
||||
return
|
||||
}
|
||||
const advanceLines = items.map((it) => `${it.applyCode}(冲抵 ${formatMoneyYuan(it.offsetAmount)})`).join('\n')
|
||||
const confirmMsg = [
|
||||
'请确认冲抵信息:',
|
||||
'',
|
||||
`冲抵金额(元):${formatMoneyYuan(totalOffset)}`,
|
||||
`本次冲抵金额(元):${formatMoneyYuan(totalOffset)}`,
|
||||
'冲抵备用金:',
|
||||
advanceLines,
|
||||
`发放金额(元):${formatMoneyYuan(payAmount)}`,
|
||||
'',
|
||||
'确定提交冲抵吗?'
|
||||
].join('\n')
|
||||
@@ -1619,12 +1654,15 @@ async function submitOffset() {
|
||||
try {
|
||||
await paymentApi.batchOffset(currentRow.value.id, {
|
||||
items: items.map(({ advanceId, offsetAmount: amt }) => ({ advanceId, offsetAmount: amt })),
|
||||
payAmount,
|
||||
payAmount: 0,
|
||||
remark: offsetRemark.value
|
||||
})
|
||||
ElMessage.success('冲抵成功')
|
||||
dialogs.offset = false
|
||||
await refresh()
|
||||
offsetAmount.value = ''
|
||||
offsetRemark.value = ''
|
||||
offsetSelectedAdvances.value = []
|
||||
uploadedFiles.value = []
|
||||
await keepOrCloseBill()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || '冲抵失败')
|
||||
} finally {
|
||||
|
||||
Reference in new issue
Block a user