结算调整单接入金蝶列表展示与冲销预检 #6
@@ -17,6 +17,10 @@ export const approve = id => request({ url: `${baseUrl}/approve`, method: 'post'
|
|||||||
export const returnBill = (id, reason) =>
|
export const returnBill = (id, reason) =>
|
||||||
request({ url: `${baseUrl}/return`, method: 'post', data: { id, reason } });
|
request({ url: `${baseUrl}/return`, method: 'post', data: { id, reason } });
|
||||||
export const repush = id => request({ url: `${baseUrl}/repush`, method: 'post', params: { id } });
|
export const repush = id => request({ url: `${baseUrl}/repush`, method: 'post', params: { id } });
|
||||||
|
export const syncKingdee = id =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', timeout: 30000, params: { id } });
|
||||||
|
export const kingdeeWriteOffPrecheck = formalSettlementId =>
|
||||||
|
request({ url: `${baseUrl}/kingdee-writeoff-precheck`, method: 'get', params: { formalSettlementId } });
|
||||||
|
|
||||||
export const approvalStatusOptions = [
|
export const approvalStatusOptions = [
|
||||||
{ label: '草稿', value: 'draft' },
|
{ label: '草稿', value: 'draft' },
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export const formalSettlementTableColumns = [
|
|||||||
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
||||||
{ label: '当前节点', prop: 'currentNode', minWidth: 130 },
|
{ label: '当前节点', prop: 'currentNode', minWidth: 130 },
|
||||||
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
||||||
|
{ label: '调整标识', prop: 'adjustFlag', minWidth: 110 },
|
||||||
|
{ label: '来源原单号', prop: 'originFormalSettlementNo', minWidth: 170 },
|
||||||
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 150 },
|
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 150 },
|
||||||
{ label: '金蝶单据状态', prop: 'kingdeeSyncStatus', minWidth: 135 },
|
{ label: '金蝶单据状态', prop: 'kingdeeSyncStatus', minWidth: 135 },
|
||||||
{ label: '金蝶锁状态', prop: 'kingdeeLockStatus', minWidth: 150 },
|
{ label: '金蝶锁状态', prop: 'kingdeeLockStatus', minWidth: 150 },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const settlementAdjustmentTableColumns = [
|
export const settlementAdjustmentTableColumns = [
|
||||||
{ label: '单据号', prop: 'adjustmentNo', minWidth: 160, link: true, fixed: 'left' },
|
{ label: '单据号', prop: 'adjustmentNo', minWidth: 160, link: true, fixed: 'left' },
|
||||||
{ label: '结算单号', prop: 'formalSettlementNo', minWidth: 160 },
|
{ label: '结算单号', prop: 'formalSettlementNo', minWidth: 160, link: true },
|
||||||
{ label: '结算单类型', prop: 'settlementTypeName', minWidth: 110 },
|
{ label: '结算单类型', prop: 'settlementTypeName', minWidth: 110 },
|
||||||
{ label: '项目名称', prop: 'projectName', minWidth: 140 },
|
{ label: '项目名称', prop: 'projectName', minWidth: 140 },
|
||||||
{ label: '所属组织', prop: 'deptName', minWidth: 140 },
|
{ label: '所属组织', prop: 'deptName', minWidth: 140 },
|
||||||
@@ -9,6 +9,8 @@ export const settlementAdjustmentTableColumns = [
|
|||||||
{ label: '合同名称', prop: 'contractName', minWidth: 170 },
|
{ label: '合同名称', prop: 'contractName', minWidth: 170 },
|
||||||
{ label: '调整金额', prop: 'adjustmentAmount', minWidth: 130, money: true },
|
{ label: '调整金额', prop: 'adjustmentAmount', minWidth: 130, money: true },
|
||||||
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
||||||
|
{ label: '金蝶单据状态', prop: 'kingdeeSyncStatus', minWidth: 130, kingdee: true },
|
||||||
|
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 150 },
|
||||||
{ label: '当前节点', prop: 'currentNode', minWidth: 120 },
|
{ label: '当前节点', prop: 'currentNode', minWidth: 120 },
|
||||||
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
||||||
{ label: '创建人', prop: 'createUserName', minWidth: 110 },
|
{ label: '创建人', prop: 'createUserName', minWidth: 110 },
|
||||||
|
|||||||
@@ -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] || '';
|
||||||
|
}
|
||||||
@@ -163,6 +163,7 @@ export default {
|
|||||||
rows: [],
|
rows: [],
|
||||||
selection: [],
|
selection: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
approveLoading: false,
|
||||||
syncLoading: false,
|
syncLoading: false,
|
||||||
resultSyncing: false,
|
resultSyncing: false,
|
||||||
query: {},
|
query: {},
|
||||||
@@ -327,14 +328,21 @@ export default {
|
|||||||
this.refreshChange();
|
this.refreshChange();
|
||||||
},
|
},
|
||||||
async handleApprove(row) {
|
async handleApprove(row) {
|
||||||
const res = await api.approve({ id: row.id });
|
// 防止重复点击:请求进行中忽略后续点击
|
||||||
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
if (this.approveLoading) return;
|
||||||
if (res.data.data === false) {
|
this.approveLoading = true;
|
||||||
this.$message.warning(res.data.msg);
|
try {
|
||||||
} else {
|
const res = await api.approve({ id: row.id });
|
||||||
this.$message.success(res.data.msg || '审批通过');
|
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审批通过');
|
||||||
|
}
|
||||||
|
this.refreshChange();
|
||||||
|
} finally {
|
||||||
|
this.approveLoading = false;
|
||||||
}
|
}
|
||||||
this.refreshChange();
|
|
||||||
},
|
},
|
||||||
handleMkAudit(row) {
|
handleMkAudit(row) {
|
||||||
if (!row?.id) {
|
if (!row?.id) {
|
||||||
|
|||||||
@@ -177,6 +177,22 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</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>
|
||||||
|
|
||||||
<section-card title="结算明细">
|
<section-card title="结算明细">
|
||||||
@@ -577,28 +593,27 @@
|
|||||||
<template #default="{ row }">{{ displayValue(row.claimStatusName) }}</template>
|
<template #default="{ row }">{{ displayValue(row.claimStatusName) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</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 type="index" label="序号" width="64" align="center" />
|
||||||
<el-table-column prop="paymentTypeName" label="付款类型" min-width="120" align="center">
|
<el-table-column label="付款类型" min-width="120" align="center">
|
||||||
<template #default="{ row }">{{ displayValue(row.paymentTypeName) }}</template>
|
<template #default="{ row }">{{ displayValue(row.typeName) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="付款方式" min-width="140" align="center">
|
<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>
|
||||||
<el-table-column prop="paymentNo" label="单据号" min-width="160" align="center" />
|
<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>
|
<template #default="{ row }">{{ formatMoney(row.appliedAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="paidAmount" label="已付款金额" min-width="130" align="center">
|
<el-table-column prop="paidAmount" label="已付款金额" min-width="130" align="center">
|
||||||
<template #default="{ row }">{{ formatMoney(row.paidAmount) }}</template>
|
<template #default="{ row }">{{ formatMoney(row.paidAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="单据状态" min-width="120" align="center">
|
||||||
prop="approvalStatusName"
|
<template #default="{ row }">
|
||||||
label="单据状态"
|
{{ row.recordType
|
||||||
min-width="120"
|
? settlementStatusName(row.billStatus)
|
||||||
align="center"
|
: displayValue(row.approvalStatusName) }}
|
||||||
>
|
</template>
|
||||||
<template #default="{ row }">{{ displayValue(row.approvalStatusName) }}</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="150" align="center">
|
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="150" align="center">
|
||||||
<template #default="{ row }">{{ displayValue(row.kingdeeBillNo) }}</template>
|
<template #default="{ row }">{{ displayValue(row.kingdeeBillNo) }}</template>
|
||||||
@@ -639,6 +654,22 @@
|
|||||||
<el-table-column prop="createTime" label="创建时间" min-width="170" align="center">
|
<el-table-column prop="createTime" label="创建时间" min-width="170" align="center">
|
||||||
<template #default="{ row }">{{ displayValue(row.createTime) }}</template>
|
<template #default="{ row }">{{ displayValue(row.createTime) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-if="hasPermission('settlement_adjustment_repush')"
|
||||||
|
label="操作"
|
||||||
|
width="120"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link
|
||||||
|
v-if="row.approvalStatus === 'approved' && row.kingdeeSyncStatus === 'synced'"
|
||||||
|
type="danger"
|
||||||
|
@click="handleAdjustmentRepush(row)"
|
||||||
|
>重新推送</el-link
|
||||||
|
>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
@@ -1038,6 +1069,10 @@ import { h } from 'vue';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import { normalizeCurrencyCode } from '@/utils/currency';
|
import { normalizeCurrencyCode } from '@/utils/currency';
|
||||||
|
import {
|
||||||
|
settlementRecordTypeName,
|
||||||
|
settlementStatusName,
|
||||||
|
} from '@/utils/settlementStatus';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
import {
|
import {
|
||||||
downloadKingdeeInvoiceFile,
|
downloadKingdeeInvoiceFile,
|
||||||
@@ -1048,6 +1083,7 @@ import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/m
|
|||||||
import ProjectAdvanceWriteOff from './project-advance-write-off.vue';
|
import ProjectAdvanceWriteOff from './project-advance-write-off.vue';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import { isAdministrator } from '@/utils/auth';
|
import { isAdministrator } from '@/utils/auth';
|
||||||
|
import * as settlementAdjustmentApi from '@/api/settlement/settlementAdjustment';
|
||||||
|
|
||||||
// 结算币种为本位币(人民币)时汇率恒为1,汇率日期、结算汇率与按汇率换算的本位币合计都不展示
|
// 结算币种为本位币(人民币)时汇率恒为1,汇率日期、结算汇率与按汇率换算的本位币合计都不展示
|
||||||
const LOCAL_CURRENCY_HIDDEN_FIELDS = ['exchangeRateDate', 'exchangeRate', 'localSettlementAmount'];
|
const LOCAL_CURRENCY_HIDDEN_FIELDS = ['exchangeRateDate', 'exchangeRate', 'localSettlementAmount'];
|
||||||
@@ -1080,6 +1116,7 @@ export default {
|
|||||||
details: [],
|
details: [],
|
||||||
summaryFees: [],
|
summaryFees: [],
|
||||||
paymentApplications: [],
|
paymentApplications: [],
|
||||||
|
advancePayments: [],
|
||||||
receiptClaims: [],
|
receiptClaims: [],
|
||||||
adjustments: [],
|
adjustments: [],
|
||||||
changeRecords: [],
|
changeRecords: [],
|
||||||
@@ -1259,6 +1296,34 @@ export default {
|
|||||||
summaryTotal() {
|
summaryTotal() {
|
||||||
return this.summaryFees.reduce((total, row) => total + Number(row.settlementAmount || 0), 0);
|
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() {
|
filteredDetails() {
|
||||||
return this.details.filter(row =>
|
return this.details.filter(row =>
|
||||||
Object.entries(this.appliedDetailQuery).every(([field, keyword]) => {
|
Object.entries(this.appliedDetailQuery).every(([field, keyword]) => {
|
||||||
@@ -1332,6 +1397,17 @@ export default {
|
|||||||
this.validData(this.permission?.[`formal_settlement_${action}`], false)
|
this.validData(this.permission?.[`formal_settlement_${action}`], false)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
async handleAdjustmentRepush(row) {
|
||||||
|
await this.$confirm(
|
||||||
|
'请确认金蝶旧应付/应收单已由财务手工冲销,继续将生成全新财务应付/应收单。',
|
||||||
|
'高危操作确认',
|
||||||
|
{ type: 'warning', confirmButtonText: '确认重新推送' }
|
||||||
|
);
|
||||||
|
const res = await settlementAdjustmentApi.repush(row.id);
|
||||||
|
this.$message.success(`已生成新金蝶单据:${res.data?.data || res.data || '-'}`);
|
||||||
|
await this.loadFormalDetail(this.recordId);
|
||||||
|
this.$emit('success');
|
||||||
|
},
|
||||||
async loadFormalDetail(id) {
|
async loadFormalDetail(id) {
|
||||||
const response = this.publicMode
|
const response = this.publicMode
|
||||||
? await getMkPublicDetail('formal-settlement', id)
|
? await getMkPublicDetail('formal-settlement', id)
|
||||||
@@ -1353,6 +1429,7 @@ export default {
|
|||||||
this.detailFeeSnapshots = {};
|
this.detailFeeSnapshots = {};
|
||||||
this.pendingAdjustments = {};
|
this.pendingAdjustments = {};
|
||||||
this.paymentApplications = data.paymentApplications || [];
|
this.paymentApplications = data.paymentApplications || [];
|
||||||
|
this.advancePayments = data.advancePayments || [];
|
||||||
if (this.readonly && this.form.settlementType === 'receivable' && !this.publicMode) {
|
if (this.readonly && this.form.settlementType === 'receivable' && !this.publicMode) {
|
||||||
this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || [];
|
this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || [];
|
||||||
}
|
}
|
||||||
@@ -1396,6 +1473,7 @@ export default {
|
|||||||
this.adjustments = [];
|
this.adjustments = [];
|
||||||
this.changeRecords = [];
|
this.changeRecords = [];
|
||||||
this.projectAdvanceWriteOffs = [];
|
this.projectAdvanceWriteOffs = [];
|
||||||
|
this.advancePayments = [];
|
||||||
this.attachments = [];
|
this.attachments = [];
|
||||||
this.selectedAttachmentRows = [];
|
this.selectedAttachmentRows = [];
|
||||||
this.invoices = [];
|
this.invoices = [];
|
||||||
@@ -1544,6 +1622,19 @@ export default {
|
|||||||
this.sources.map(item => getPreSettlementDetail(item.preSettlementId))
|
this.sources.map(item => getPreSettlementDetail(item.preSettlementId))
|
||||||
);
|
);
|
||||||
const settlements = responses.map(response => this.unwrapData(response) || {});
|
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 =>
|
this.details = settlements.flatMap(settlement =>
|
||||||
(settlement.details || []).map(detail => ({
|
(settlement.details || []).map(detail => ({
|
||||||
...detail,
|
...detail,
|
||||||
@@ -1714,7 +1805,7 @@ export default {
|
|||||||
this.candidate.page.current = 1;
|
this.candidate.page.current = 1;
|
||||||
this.loadCandidates();
|
this.loadCandidates();
|
||||||
},
|
},
|
||||||
confirmCandidates() {
|
async confirmCandidates() {
|
||||||
const merged = [...this.sources, ...this.candidate.selected];
|
const merged = [...this.sources, ...this.candidate.selected];
|
||||||
const map = new Map(
|
const map = new Map(
|
||||||
merged.map(item => [
|
merged.map(item => [
|
||||||
@@ -1722,6 +1813,7 @@ export default {
|
|||||||
{ ...item, preSettlementId: item.preSettlementId || item.id },
|
{ ...item, preSettlementId: item.preSettlementId || item.id },
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
const previousIds = new Set(this.sources.map(item => String(item.preSettlementId || item.id)));
|
||||||
this.sources = [...map.values()];
|
this.sources = [...map.values()];
|
||||||
this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId);
|
this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId);
|
||||||
if (this.sources.length) {
|
if (this.sources.length) {
|
||||||
@@ -1738,6 +1830,26 @@ export default {
|
|||||||
this.form.localSettlementAmount =
|
this.form.localSettlementAmount =
|
||||||
this.form.settlementAmount * Number(this.form.exchangeRate || 1);
|
this.form.settlementAmount * Number(this.form.exchangeRate || 1);
|
||||||
this.candidate.visible = false;
|
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) {
|
removeSource(index) {
|
||||||
this.sources.splice(index, 1);
|
this.sources.splice(index, 1);
|
||||||
@@ -2762,6 +2874,9 @@ export default {
|
|||||||
};
|
};
|
||||||
return this.displayValue(labels[value] || value);
|
return this.displayValue(labels[value] || value);
|
||||||
},
|
},
|
||||||
|
settlementStatusName(status) {
|
||||||
|
return settlementStatusName(status, this.displayValue);
|
||||||
|
},
|
||||||
unwrapData(response) {
|
unwrapData(response) {
|
||||||
const body = response?.data || response || {};
|
const body = response?.data || response || {};
|
||||||
return body?.data || body;
|
return body?.data || body;
|
||||||
@@ -3144,6 +3259,9 @@ export default {
|
|||||||
.formal-editor__negative-amount {
|
.formal-editor__negative-amount {
|
||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
}
|
}
|
||||||
|
.formal-editor__settle-summary {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
.formal-editor :deep(.formal-editor__negative-amount .el-input__inner) {
|
.formal-editor :deep(.formal-editor__negative-amount .el-input__inner) {
|
||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,7 @@
|
|||||||
<span v-else-if="column.prop === 'kingdeeSyncStatus'">{{
|
<span v-else-if="column.prop === 'kingdeeSyncStatus'">{{
|
||||||
kingdeeSyncName(row.kingdeeSyncStatus)
|
kingdeeSyncName(row.kingdeeSyncStatus)
|
||||||
}}</span>
|
}}</span>
|
||||||
|
<span v-else-if="column.prop === 'adjustFlag'">{{ adjustFlag(row) }}</span>
|
||||||
<template v-else-if="column.prop === 'kingdeeLockStatus'">
|
<template v-else-if="column.prop === 'kingdeeLockStatus'">
|
||||||
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
|
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
|
||||||
<el-tag :type="lockTag(row).type" class="status-text">{{ lockTag(row).label }}</el-tag>
|
<el-tag :type="lockTag(row).type" class="status-text">{{ lockTag(row).label }}</el-tag>
|
||||||
@@ -277,6 +278,12 @@ export default {
|
|||||||
kingdeeSyncName(value) {
|
kingdeeSyncName(value) {
|
||||||
return kingdeeSyncStatusOptions.find(item => item.value === value)?.label || value || '-';
|
return kingdeeSyncStatusOptions.find(item => item.value === value)?.label || value || '-';
|
||||||
},
|
},
|
||||||
|
adjustFlag(row) {
|
||||||
|
// 原单已调整 / 重推生成的新单(显示来源原单号)
|
||||||
|
if (row.adjustStatusName) return row.adjustStatusName;
|
||||||
|
if (row.originFormalSettlementNo) return '调整后单';
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
lockTag(row) {
|
lockTag(row) {
|
||||||
if (row.kingdeeLockStatus === 'locked') {
|
if (row.kingdeeLockStatus === 'locked') {
|
||||||
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
|
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
|
||||||
|
|||||||
@@ -987,6 +987,7 @@ import {
|
|||||||
} from '@/option/settlement/preSettlementTable';
|
} from '@/option/settlement/preSettlementTable';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import { normalizeCurrencyCode } from '@/utils/currency';
|
import { normalizeCurrencyCode } from '@/utils/currency';
|
||||||
|
import { settlementStatusName } from '@/utils/settlementStatus';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
@@ -2919,15 +2920,7 @@ export default {
|
|||||||
return value === undefined || value === null || value === '' ? '-' : value;
|
return value === undefined || value === null || value === '' ? '-' : value;
|
||||||
},
|
},
|
||||||
advanceStatusName(status) {
|
advanceStatusName(status) {
|
||||||
return (
|
return settlementStatusName(status, this.displayValue);
|
||||||
{
|
|
||||||
reviewing: '审批中',
|
|
||||||
approved: '审批通过',
|
|
||||||
paid: '已付款',
|
|
||||||
returned: '已驳回',
|
|
||||||
voided: '已作废',
|
|
||||||
}[status] || this.displayValue(status)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
advanceCreateUserName(row) {
|
advanceCreateUserName(row) {
|
||||||
return this.displayValue(
|
return this.displayValue(
|
||||||
|
|||||||
@@ -414,6 +414,8 @@ export default {
|
|||||||
});
|
});
|
||||||
this.details = [];
|
this.details = [];
|
||||||
this.recalculate();
|
this.recalculate();
|
||||||
|
// 金蝶冲销预检:软提示,不阻断建单(金蝶不可用时静默降级)
|
||||||
|
this.precheckWriteOff(formalSettlementId);
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const { data } = await api.getFormalDetails(formalSettlementId);
|
const { data } = await api.getFormalDetails(formalSettlementId);
|
||||||
@@ -431,6 +433,19 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async precheckWriteOff(formalSettlementId) {
|
||||||
|
try {
|
||||||
|
const { data } = await api.kingdeeWriteOffPrecheck(formalSettlementId);
|
||||||
|
if (String(this.form.formalSettlementId) !== String(formalSettlementId)) return;
|
||||||
|
const result = data?.data || data || {};
|
||||||
|
if (!result.message) return;
|
||||||
|
if (result.syncStatus === 'synced' && result.writtenOff === false)
|
||||||
|
this.$message.warning(result.message);
|
||||||
|
else this.$message.info(result.message);
|
||||||
|
} catch (error) {
|
||||||
|
// 预检软提示:金蝶查询失败静默降级,不阻断建单
|
||||||
|
}
|
||||||
|
},
|
||||||
addFeeRow() {
|
addFeeRow() {
|
||||||
this.details.push({
|
this.details.push({
|
||||||
formalSettlementDetailId: null,
|
formalSettlementDetailId: null,
|
||||||
|
|||||||
@@ -297,6 +297,10 @@ export default {
|
|||||||
if (detailNo) this.openRouteDetail(detailNo);
|
if (detailNo) this.openRouteDetail(detailNo);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
// 标签机制按 fullPath 缓存同页多实例:仅当前路由实例应响应 detailNo 跳转,防止缓存旧实例也弹窗
|
||||||
|
this.ownFullPath = this.$route.fullPath;
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.lockedSettlementType === 'payable' || this.lockedSettlementType === 'receivable') {
|
if (this.lockedSettlementType === 'payable' || this.lockedSettlementType === 'receivable') {
|
||||||
this.activeSettlementType = this.lockedSettlementType;
|
this.activeSettlementType = this.lockedSettlementType;
|
||||||
@@ -397,7 +401,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async openRouteDetail(detailNo = this.$route.query.detailNo) {
|
async openRouteDetail(detailNo = this.$route.query.detailNo) {
|
||||||
const formalSettlementNo = String(detailNo || '').trim();
|
const formalSettlementNo = String(detailNo || '').trim();
|
||||||
if (!formalSettlementNo) return;
|
if (!formalSettlementNo || this.ownFullPath !== this.$route.fullPath) return;
|
||||||
const response = await api.getList(1, 10, { formalSettlementNo });
|
const response = await api.getList(1, 10, { formalSettlementNo });
|
||||||
const data = this.unwrapData(response);
|
const data = this.unwrapData(response);
|
||||||
const row = (data.records || []).find(item => item.formalSettlementNo === formalSettlementNo);
|
const row = (data.records || []).find(item => item.formalSettlementNo === formalSettlementNo);
|
||||||
|
|||||||
@@ -88,16 +88,21 @@
|
|||||||
><el-link
|
><el-link
|
||||||
v-if="column.link && row[column.prop]"
|
v-if="column.link && row[column.prop]"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openView(row)"
|
@click="handleTableLink(row, column)"
|
||||||
>{{ row[column.prop] }}</el-link
|
>{{ row[column.prop] }}</el-link
|
||||||
><el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">{{
|
><el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">{{
|
||||||
row[column.prop] || '-'
|
row[column.prop] || '-'
|
||||||
}}</el-tag
|
}}</el-tag
|
||||||
|
><el-tag
|
||||||
|
v-else-if="column.kingdee"
|
||||||
|
:type="kingdeeStatusType(row.kingdeeSyncStatus)"
|
||||||
|
class="status-text"
|
||||||
|
>{{ kingdeeSyncName(row.kingdeeSyncStatus) }}</el-tag
|
||||||
><span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span
|
><span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span
|
||||||
><span v-else>{{ displayValue(row[column.prop]) }}</span></template
|
><span v-else>{{ displayValue(row[column.prop]) }}</span></template
|
||||||
></el-table-column
|
></el-table-column
|
||||||
>
|
>
|
||||||
<el-table-column label="操作" width="120" fixed="right" align="center"
|
<el-table-column label="操作" width="320" fixed="right" align="center"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
><div class="settlement-adjustment-page__links">
|
><div class="settlement-adjustment-page__links">
|
||||||
<el-link
|
<el-link
|
||||||
@@ -139,14 +144,10 @@
|
|||||||
@click="handleReturn(row)"
|
@click="handleReturn(row)"
|
||||||
>驳回</el-link
|
>驳回</el-link
|
||||||
><el-link
|
><el-link
|
||||||
v-if="
|
v-if="hasPermission('settlement_adjustment_repush') && row.approvalStatus === 'approved'"
|
||||||
hasPermission('settlement_adjustment_repush') &&
|
:type="row.kingdeeSyncStatus === 'synced' ? 'danger' : 'primary'"
|
||||||
row.approvalStatus === 'approved' &&
|
@click="handleKingdee(row)"
|
||||||
row.kingdeeSyncStatus === 'synced'
|
>{{ row.kingdeeSyncStatus === 'synced' ? '重新推送(高危)' : '推送金蝶' }}</el-link
|
||||||
"
|
|
||||||
type="danger"
|
|
||||||
@click="handleRepush(row)"
|
|
||||||
>重新推送(高危)</el-link
|
|
||||||
>
|
>
|
||||||
</div></template
|
</div></template
|
||||||
></el-table-column
|
></el-table-column
|
||||||
@@ -180,6 +181,7 @@ import * as api from '@/api/settlement/settlementAdjustment';
|
|||||||
import { settlementAdjustmentSearchFields } from '@/option/settlement/settlementAdjustmentSearch';
|
import { settlementAdjustmentSearchFields } from '@/option/settlement/settlementAdjustmentSearch';
|
||||||
import organizationSearch from '@/mixins/organization-search';
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
import { settlementAdjustmentTableColumns } from '@/option/settlement/settlementAdjustmentTable';
|
import { settlementAdjustmentTableColumns } from '@/option/settlement/settlementAdjustmentTable';
|
||||||
|
import { kingdeeSyncStatusOptions } from '@/option/settlement/formalSettlementSearch';
|
||||||
import SettlementAdjustmentEditor from './components/settlement-adjustment-editor.vue';
|
import SettlementAdjustmentEditor from './components/settlement-adjustment-editor.vue';
|
||||||
import SettlementTableTools from './components/settlement-table-tools.vue';
|
import SettlementTableTools from './components/settlement-table-tools.vue';
|
||||||
import { isAdministrator } from '@/utils/auth';
|
import { isAdministrator } from '@/utils/auth';
|
||||||
@@ -283,6 +285,21 @@ export default {
|
|||||||
openView(row) {
|
openView(row) {
|
||||||
this.editor = { visible: true, id: row.id, readonly: true };
|
this.editor = { visible: true, id: row.id, readonly: true };
|
||||||
},
|
},
|
||||||
|
// 单据号→本单查看弹窗;结算单号→跳正式结算列表页并自动打开该单详情(页面支持 ?detailNo=)
|
||||||
|
handleTableLink(row, column) {
|
||||||
|
if (column.prop === 'formalSettlementNo') {
|
||||||
|
this.openSettlementDetail(row.formalSettlementNo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openView(row);
|
||||||
|
},
|
||||||
|
openSettlementDetail(detailNo) {
|
||||||
|
if (!detailNo) {
|
||||||
|
this.$message.info('当前记录未关联结算单详情');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$router.push({ path: '/settlement/formal-settlement', query: { detailNo } });
|
||||||
|
},
|
||||||
isEditable(row) {
|
isEditable(row) {
|
||||||
return ['draft', 'returned'].includes(row.approvalStatus);
|
return ['draft', 'returned'].includes(row.approvalStatus);
|
||||||
},
|
},
|
||||||
@@ -300,12 +317,10 @@ export default {
|
|||||||
},
|
},
|
||||||
async handleApprove(row) {
|
async handleApprove(row) {
|
||||||
await this.$confirm('审批通过后将更新关联正式结算单,确认继续?', '提示');
|
await this.$confirm('审批通过后将更新关联正式结算单,确认继续?', '提示');
|
||||||
await api.approve(row.id);
|
const res = await api.approve(row.id);
|
||||||
if (row.kingdeeSyncStatus === 'synced')
|
// data=false:自动同步金蝶失败;data=true 且 msg 带已推送场景的手工冲销提示
|
||||||
this.$message.warning(
|
if (res.data.data === true || res.data.data === false) this.$message.warning(res.data.msg);
|
||||||
'调整已生效,本地已更新;原结算单已推送金蝶,请财务先手工冲销旧应付单后再重新推送。'
|
else this.$message.success(res.data.msg || '审批通过');
|
||||||
);
|
|
||||||
else this.$message.success('审批通过');
|
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
async handleReturn(row) {
|
async handleReturn(row) {
|
||||||
@@ -317,14 +332,24 @@ export default {
|
|||||||
this.$message.success('已驳回');
|
this.$message.success('已驳回');
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
|
// 单一入口按状态分流:未同步/失败→推送原结算单;已同步→冲销后重推新单(高危)
|
||||||
|
async handleKingdee(row) {
|
||||||
|
if (row.kingdeeSyncStatus === 'synced') return this.handleRepush(row);
|
||||||
|
return this.handleSyncKingdee(row);
|
||||||
|
},
|
||||||
|
async handleSyncKingdee(row) {
|
||||||
|
const res = await api.syncKingdee(row.id);
|
||||||
|
this.$message.success(`已推送金蝶:${res.data.data || '-'}`);
|
||||||
|
this.loadTable();
|
||||||
|
},
|
||||||
async handleRepush(row) {
|
async handleRepush(row) {
|
||||||
await this.$confirm(
|
await this.$confirm(
|
||||||
'请确认金蝶旧应付单已由财务手工冲销,继续将生成全新财务应付单。',
|
'请确认金蝶旧应付/应收单已由财务手工冲销,继续将生成全新财务应付/应收单。',
|
||||||
'高危操作确认',
|
'高危操作确认',
|
||||||
{ type: 'warning', confirmButtonText: '确认重新推送' }
|
{ type: 'warning', confirmButtonText: '确认重新推送' }
|
||||||
);
|
);
|
||||||
const { data } = await api.repush(row.id);
|
const { data } = await api.repush(row.id);
|
||||||
this.$message.success(`已生成新金蝶应付单:${data?.data || data || '-'}`);
|
this.$message.success(`已生成新金蝶单据:${data?.data || data || '-'}`);
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
statusType(status) {
|
statusType(status) {
|
||||||
@@ -333,6 +358,12 @@ export default {
|
|||||||
'info'
|
'info'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
kingdeeStatusType(status) {
|
||||||
|
return { synced: 'success', failed: 'danger' }[status] || 'info';
|
||||||
|
},
|
||||||
|
kingdeeSyncName(value) {
|
||||||
|
return kingdeeSyncStatusOptions.find(item => item.value === value)?.label || value || '-';
|
||||||
|
},
|
||||||
displayValue(value) {
|
displayValue(value) {
|
||||||
return value === null || value === undefined || value === '' ? '-' : value;
|
return value === null || value === undefined || value === '' ? '-' : value;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,5 +42,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.business-panel {
|
.business-panel {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.finance-panel {
|
.finance-panel {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.fleet-panel {
|
.fleet-panel {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.kpi-card {
|
.kpi-card {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.project-panel {
|
.project-panel {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.structure-cards__item {
|
.structure-cards__item {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.summary-panel {
|
.summary-panel {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.vehicle-cards__item {
|
.vehicle-cards__item {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../dashboard.scss';
|
@use '../dashboard.scss';
|
||||||
|
|
||||||
.wel-filter__range {
|
.wel-filter__range {
|
||||||
width: 110px;
|
width: 110px;
|
||||||
|
|||||||
Reference in New Issue
Block a user