feat: 正式结算单新增预付汇总栏与付款记录合并展示

结算合计下新增汇总栏(结算合计原金额-已付金额含预付核销-剩余未付);只读付款信息合并展示进度预付-项目预付核销-结算尾款三类记录;预结算转正式自动预填来源预结算核销行;单据状态名提取公共 settlementStatus 工具与预结算编辑器共用
This commit is contained in:
刘泉佳
2026-10-18 14:32:15 +08:00
parent 73c12cc1c7
commit 6596722601
3 changed files with 128 additions and 22 deletions
+23
View File
@@ -0,0 +1,23 @@
/**
* 结算单预付记录单据状态名(预结算编辑器与正式结算编辑器共用,避免两页状态名不一致)
*/
export function settlementStatusName(status, displayValue) {
const labels = {
reviewing: '审批中',
approved: '审批通过',
paid: '已付款',
returned: '已驳回',
voided: '已作废',
draft: '草稿',
};
const name = labels[status];
if (name) return name;
return typeof displayValue === 'function' ? displayValue(status) : status || '-';
}
/**
* 统一预付付款记录类型名
*/
export function settlementRecordTypeName(recordType) {
return { progress_advance: '进度预付', project_advance: '项目预付核销' }[recordType] || '';
}
@@ -177,6 +177,22 @@
</template>
</el-table-column>
</el-table>
<el-descriptions
v-if="form.settlementType === 'payable'"
:column="3"
border
class="formal-editor__settle-summary"
>
<el-descriptions-item label="结算合计(原金额)">
{{ formatMoney(form.settlementAmount) }}
</el-descriptions-item>
<el-descriptions-item label="已付金额(含预付、核销)">
{{ formatMoney(settlePaidTotal) }}
</el-descriptions-item>
<el-descriptions-item label="剩余未付金额">
{{ formatMoney(settleRemainTotal) }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="结算明细">
@@ -577,28 +593,27 @@
<template #default="{ row }">{{ displayValue(row.claimStatusName) }}</template>
</el-table-column>
</el-table>
<el-table v-else :data="paymentApplications" border>
<el-table v-else :data="paymentRecords" border>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column prop="paymentTypeName" label="付款类型" min-width="120" align="center">
<template #default="{ row }">{{ displayValue(row.paymentTypeName) }}</template>
<el-table-column label="付款类型" min-width="120" align="center">
<template #default="{ row }">{{ displayValue(row.typeName) }}</template>
</el-table-column>
<el-table-column label="付款方式" min-width="140" align="center">
<template #default="{ row }">{{ paymentMethodName(row.paymentMethod) }}</template>
<template #default="{ row }">{{ row.recordType ? '-' : paymentMethodName(row.paymentMethod) }}</template>
</el-table-column>
<el-table-column prop="paymentNo" label="单据号" min-width="160" align="center" />
<el-table-column prop="appliedAmount" label="申请付款金额" min-width="145" align="center">
<el-table-column prop="appliedAmount" label="申请/核销金额" min-width="145" align="center">
<template #default="{ row }">{{ formatMoney(row.appliedAmount) }}</template>
</el-table-column>
<el-table-column prop="paidAmount" label="已付款金额" min-width="130" align="center">
<template #default="{ row }">{{ formatMoney(row.paidAmount) }}</template>
</el-table-column>
<el-table-column
prop="approvalStatusName"
label="单据状态"
min-width="120"
align="center"
>
<template #default="{ row }">{{ displayValue(row.approvalStatusName) }}</template>
<el-table-column label="单据状态" min-width="120" align="center">
<template #default="{ row }">
{{ row.recordType
? settlementStatusName(row.billStatus)
: displayValue(row.approvalStatusName) }}
</template>
</el-table-column>
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="150" align="center">
<template #default="{ row }">{{ displayValue(row.kingdeeBillNo) }}</template>
@@ -1054,6 +1069,10 @@ import { InfoFilled } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import { formatContractOptionLabel } from '@/utils/contract';
import { normalizeCurrencyCode } from '@/utils/currency';
import {
settlementRecordTypeName,
settlementStatusName,
} from '@/utils/settlementStatus';
import { downloadFileByUrl } from '@/utils/util';
import {
downloadKingdeeInvoiceFile,
@@ -1097,6 +1116,7 @@ export default {
details: [],
summaryFees: [],
paymentApplications: [],
advancePayments: [],
receiptClaims: [],
adjustments: [],
changeRecords: [],
@@ -1270,6 +1290,34 @@ export default {
summaryTotal() {
return this.summaryFees.reduce((total, row) => total + Number(row.settlementAmount || 0), 0);
},
// 已付金额(含预付、核销):编辑态取来源预结算预付快照 + 当前核销行实时合计(草稿态无尾款申请);只读态用落库值
settlePaidTotal() {
if (!this.editable) return Number(this.form.paidAmount || 0);
const advancePaid = this.sources.reduce(
(sum, item) => sum + Number(item.advancePaidAmount || 0),
0
);
const writeOff = this.projectAdvanceWriteOffs.reduce(
(sum, row) => sum + Number(row.writeOffAmount || 0),
0
);
return advancePaid + writeOff;
},
settleRemainTotal() {
return Math.max(Number(this.form.settlementAmount || 0) - Number(this.settlePaidTotal || 0), 0);
},
// 付款记录合并表:预付付款记录(进度预付/项目预付核销)+ 尾款付款申请
paymentRecords() {
const advances = (this.advancePayments || []).map(row => ({
...row,
typeName: settlementRecordTypeName(row.recordType),
}));
const tails = (this.paymentApplications || []).map(row => ({
...row,
typeName: row.paymentTypeName,
}));
return [...advances, ...tails];
},
filteredDetails() {
return this.details.filter(row =>
Object.entries(this.appliedDetailQuery).every(([field, keyword]) => {
@@ -1365,6 +1413,7 @@ export default {
this.detailFeeSnapshots = {};
this.pendingAdjustments = {};
this.paymentApplications = data.paymentApplications || [];
this.advancePayments = data.advancePayments || [];
if (this.readonly && this.form.settlementType === 'receivable' && !this.publicMode) {
this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || [];
}
@@ -1408,6 +1457,7 @@ export default {
this.adjustments = [];
this.changeRecords = [];
this.projectAdvanceWriteOffs = [];
this.advancePayments = [];
this.attachments = [];
this.selectedAttachmentRows = [];
this.invoices = [];
@@ -1556,6 +1606,19 @@ export default {
this.sources.map(item => getPreSettlementDetail(item.preSettlementId))
);
const settlements = responses.map(response => this.unwrapData(response) || {});
// 预结算转正式:带出来源预结算已保存的项目预付核销行(后端保存时转移源行)
const writeOffRows = settlements.flatMap(settlement => settlement.projectAdvanceWriteOffs || []);
const existingWriteOffIds = new Set(
this.projectAdvanceWriteOffs.map(row => String(row.paymentApplicationId))
);
writeOffRows.forEach(row => {
if (existingWriteOffIds.has(String(row.paymentApplicationId))) return;
existingWriteOffIds.add(String(row.paymentApplicationId));
this.projectAdvanceWriteOffs.push({
...row,
writeOffAmount: Number(row.writeOffAmount || 0),
});
});
this.details = settlements.flatMap(settlement =>
(settlement.details || []).map(detail => ({
...detail,
@@ -1726,7 +1789,7 @@ export default {
this.candidate.page.current = 1;
this.loadCandidates();
},
confirmCandidates() {
async confirmCandidates() {
const merged = [...this.sources, ...this.candidate.selected];
const map = new Map(
merged.map(item => [
@@ -1734,6 +1797,7 @@ export default {
{ ...item, preSettlementId: item.preSettlementId || item.id },
])
);
const previousIds = new Set(this.sources.map(item => String(item.preSettlementId || item.id)));
this.sources = [...map.values()];
this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId);
if (this.sources.length) {
@@ -1750,6 +1814,26 @@ export default {
this.form.localSettlementAmount =
this.form.settlementAmount * Number(this.form.exchangeRate || 1);
this.candidate.visible = false;
// 预结算转正式:带出来源预结算已保存的项目预付核销行(后端保存时转移源行,额度单份占用)
const newIds = this.sources
.map(item => item.preSettlementId)
.filter(id => !previousIds.has(String(id)));
if (!newIds.length) return;
const responses = await Promise.all(newIds.map(id => getPreSettlementDetail(id)));
const rows = responses.flatMap(response =>
this.unwrapData(response)?.projectAdvanceWriteOffs || []
);
const existing = new Set(
this.projectAdvanceWriteOffs.map(row => String(row.paymentApplicationId))
);
rows.forEach(row => {
if (existing.has(String(row.paymentApplicationId))) return;
existing.add(String(row.paymentApplicationId));
this.projectAdvanceWriteOffs.push({
...row,
writeOffAmount: Number(row.writeOffAmount || 0),
});
});
},
removeSource(index) {
this.sources.splice(index, 1);
@@ -2736,6 +2820,9 @@ export default {
};
return this.displayValue(labels[value] || value);
},
settlementStatusName(status) {
return settlementStatusName(status, this.displayValue);
},
unwrapData(response) {
const body = response?.data || response || {};
return body?.data || body;
@@ -3132,6 +3219,9 @@ export default {
.formal-editor__negative-amount {
color: var(--el-color-danger);
}
.formal-editor__settle-summary {
margin-top: 8px;
}
.formal-editor :deep(.formal-editor__negative-amount .el-input__inner) {
color: var(--el-color-danger);
}
@@ -990,6 +990,7 @@ import {
} from '@/option/settlement/preSettlementTable';
import { formatContractOptionLabel } from '@/utils/contract';
import { normalizeCurrencyCode } from '@/utils/currency';
import { settlementStatusName } from '@/utils/settlementStatus';
import { downloadFileByUrl } from '@/utils/util';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import * as XLSX from 'xlsx';
@@ -2877,15 +2878,7 @@ export default {
return value === undefined || value === null || value === '' ? '-' : value;
},
advanceStatusName(status) {
return (
{
reviewing: '审批中',
approved: '审批通过',
paid: '已付款',
returned: '已驳回',
voided: '已作废',
}[status] || this.displayValue(status)
);
return settlementStatusName(status, this.displayValue);
},
advanceCreateUserName(row) {
return this.displayValue(