feat: 付款结算单选收票单全额认领并新增发票登记推送指定应付
This commit is contained in:
@@ -440,8 +440,15 @@
|
||||
/>
|
||||
|
||||
<section-card title="发票信息">
|
||||
<div class="formal-editor__invoice-claim">
|
||||
<el-button type="primary" @click="openInvoiceClaimDialog">认领查询</el-button>
|
||||
<div v-if="editable" class="formal-editor__invoice-claim">
|
||||
<!-- 先票后款:付款结算单选审批通过的收票单(全额认领);收款结算单维持原票池认领 -->
|
||||
<el-button
|
||||
v-if="form.settlementType !== 'receivable'"
|
||||
type="primary"
|
||||
@click="openInvoiceReceiptDialog"
|
||||
>选择收票单</el-button
|
||||
>
|
||||
<el-button v-else type="primary" @click="openInvoiceClaimDialog">认领查询</el-button>
|
||||
</div>
|
||||
<el-table :data="invoices" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
@@ -452,11 +459,6 @@
|
||||
<el-table-column prop="invoiceType" label="发票类型" min-width="150" align="center">
|
||||
<template #default="{ row }">{{ displayValue(row.invoiceType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taxRate" label="税率" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.taxRate === null || row.taxRate === undefined ? '-' : `${row.taxRate}%` }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="invoiceAmount"
|
||||
label="发票金额(含税)"
|
||||
@@ -480,8 +482,9 @@
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<!-- 先票后款:收票单认领金额固定=发票金额(全额认领),不可改 -->
|
||||
<el-input-number
|
||||
v-if="editable"
|
||||
v-if="editable && form.settlementType === 'receivable'"
|
||||
v-model="row.matchedAmount"
|
||||
:min="0"
|
||||
:max="Number(row.availableInvoiceAmount || 0)"
|
||||
@@ -546,6 +549,11 @@
|
||||
@confirm="handleInvoiceClaimConfirm"
|
||||
/>
|
||||
|
||||
<invoice-receipt-candidates-dialog
|
||||
v-model="invoiceReceiptDialog.visible"
|
||||
@confirm="handleInvoiceReceiptConfirm"
|
||||
/>
|
||||
|
||||
<section-card
|
||||
v-if="readonly"
|
||||
:title="form.settlementType === 'receivable' ? '收款信息' : '付款信息'"
|
||||
@@ -1068,6 +1076,7 @@ import {
|
||||
settlementDetailPath,
|
||||
} from '@/utils/settlement-detail-link';
|
||||
import { getBfiExchangeRate } from '@/api/base/currency';
|
||||
import InvoiceReceiptCandidatesDialog from './invoice-receipt-candidates-dialog.vue';
|
||||
import {
|
||||
createFormalSettlementForm,
|
||||
formalSettlementFormFields,
|
||||
@@ -1110,6 +1119,7 @@ export default {
|
||||
components: {
|
||||
InfoFilled,
|
||||
KingdeeInvoicePoolDialog,
|
||||
InvoiceReceiptCandidatesDialog,
|
||||
ChangeRecordDetailDialog,
|
||||
ProjectAdvanceWriteOff,
|
||||
},
|
||||
@@ -1158,6 +1168,10 @@ export default {
|
||||
selected: [],
|
||||
confirming: false,
|
||||
},
|
||||
// 先票后款:付款结算单选收票单(审批通过+未认领)弹窗
|
||||
invoiceReceiptDialog: {
|
||||
visible: false,
|
||||
},
|
||||
attachmentUploadFiles: [],
|
||||
attachmentTypeOptions: [],
|
||||
attachmentFileTypes: [
|
||||
@@ -2651,6 +2665,39 @@ export default {
|
||||
openInvoiceClaimDialog() {
|
||||
this.invoiceClaimDialog.visible = true;
|
||||
},
|
||||
// 先票后款:选收票单(审批通过+未被认领),确认后按发票号全额认领
|
||||
openInvoiceReceiptDialog() {
|
||||
this.invoiceReceiptDialog.visible = true;
|
||||
},
|
||||
async handleInvoiceReceiptConfirm(rows) {
|
||||
// 收票单行 → 结算单发票行:匹配金额固定=发票金额(全额认领),附件契约归一为单数 attachmentJson
|
||||
const invoices = rows.map(row => ({
|
||||
...row,
|
||||
attachmentJson: row.attachmentJson || row.attachmentsJson,
|
||||
invoiceType: row.invoiceType || '',
|
||||
taxRate: row.taxRate ?? null,
|
||||
availableInvoiceAmount: Number(row.invoiceAmount || 0),
|
||||
matchedAmount: Number(row.invoiceAmount || 0),
|
||||
}));
|
||||
if (!this.form.id) {
|
||||
const existingNumbers = new Set(this.invoices.map(item => String(item.invoiceNo)));
|
||||
this.invoices = [
|
||||
...this.invoices,
|
||||
...invoices.filter(row => !existingNumbers.has(String(row.invoiceNo))),
|
||||
];
|
||||
this.$message.success(`已选择${invoices.length}条收票单,保存正式结算单后生效`);
|
||||
return;
|
||||
}
|
||||
const existingNumbers = new Set(this.invoices.map(item => String(item.invoiceNo)));
|
||||
const newInvoices = invoices.filter(row => !existingNumbers.has(String(row.invoiceNo)));
|
||||
if (!newInvoices.length) {
|
||||
this.$message.warning('所选收票单均已认领');
|
||||
return;
|
||||
}
|
||||
await claimInvoicesApi({ formalSettlementId: this.form.id, invoices: newInvoices });
|
||||
await this.loadFormalDetail(this.form.id);
|
||||
this.$message.success('收票单认领成功,已同步正式结算单及关联付款单据');
|
||||
},
|
||||
invoicePoolQueryApi(params) {
|
||||
return queryInvoicePool(params);
|
||||
},
|
||||
|
||||
@@ -189,6 +189,13 @@ export default {
|
||||
row.kingdeeSyncStatus !== 'synced'
|
||||
)
|
||||
actions.push({ type: 'void', label: '作废', danger: true });
|
||||
// 先票后款:付款结算单审批通过后可发票登记(关联收票单并推送指定应付)
|
||||
if (
|
||||
this.showPaymentAction &&
|
||||
this.hasPermission('invoice_register') &&
|
||||
row.approvalStatus === 'approved'
|
||||
)
|
||||
actions.push({ type: 'invoiceRegister', label: '发票登记' });
|
||||
return actions;
|
||||
},
|
||||
statusType(status) {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="选择收票单"
|
||||
width="86%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@open="handleOpen"
|
||||
>
|
||||
<div class="invoice-candidates-dialog__search">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
clearable
|
||||
placeholder="发票号 / 销方(开票单位) / 购方(受票单位)"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-button type="primary" :loading="loading" @click="handleQuery">查询</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
ref="candidatesTable"
|
||||
v-loading="loading"
|
||||
:data="rows"
|
||||
row-key="id"
|
||||
border
|
||||
height="460"
|
||||
@selection-change="selection = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="52" fixed="left" align="center" reserve-selection />
|
||||
<el-table-column prop="invoiceNo" label="发票号码" min-width="180" />
|
||||
<el-table-column prop="invoiceDate" label="开票日期" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.invoiceDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="invoiceType" label="发票类型" min-width="140" />
|
||||
<el-table-column prop="issuerName" label="开票单位(销方)" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="receiverName" label="受票单位(购方)" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="发票金额(含税)" min-width="140" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="推送状态" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="pushStatusType(row.kingdeePushStatus)" size="small">
|
||||
{{ pushStatusName(row.kingdeePushStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="invoice-candidates-dialog__tip">
|
||||
仅列出审批通过且未被其他付款结算单认领的收票单,最多 200 条
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">
|
||||
确定{{ selection.length ? `(${selection.length})` : '' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { claimCandidates } from '@/api/payment/invoiceReceipt';
|
||||
|
||||
export default {
|
||||
name: 'InvoiceReceiptCandidatesDialog',
|
||||
props: {
|
||||
modelValue: { type: Boolean, required: true },
|
||||
},
|
||||
emits: ['update:modelValue', 'confirm'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
keyword: '',
|
||||
rows: [],
|
||||
selection: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visible: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
unwrap(response) {
|
||||
const body = response?.data || response || {};
|
||||
return body?.data || body;
|
||||
},
|
||||
handleOpen() {
|
||||
this.keyword = '';
|
||||
this.selection = [];
|
||||
this.$refs.candidatesTable?.clearSelection?.();
|
||||
this.load();
|
||||
},
|
||||
handleQuery() {
|
||||
this.load();
|
||||
},
|
||||
async load() {
|
||||
this.loading = true;
|
||||
try {
|
||||
this.rows = this.unwrap(await claimCandidates(this.keyword)) || [];
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
handleConfirm() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.warning('请至少选择一条收票单');
|
||||
return;
|
||||
}
|
||||
this.$emit('confirm', this.selection.map(row => ({ ...row })));
|
||||
this.visible = false;
|
||||
},
|
||||
pushStatusName(value) {
|
||||
return (
|
||||
{ pushed: '已保存收票单', assigned: '已指定应付', failed: '推送失败', unsynced: '未推送' }[
|
||||
value
|
||||
] || '未推送'
|
||||
);
|
||||
},
|
||||
pushStatusType(value) {
|
||||
return { pushed: 'warning', assigned: 'success', failed: 'danger', unsynced: 'info' }[value] || 'info';
|
||||
},
|
||||
formatMoney(value) {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number.toFixed(2) : '-';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-candidates-dialog__search {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.el-input {
|
||||
width: 360px;
|
||||
}
|
||||
}
|
||||
.invoice-candidates-dialog__tip {
|
||||
margin-top: 8px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user