Merge branch 'master' into feature/20261019-bfi-all-web
# Conflicts: # src/views/payment/invoice-application.vue # src/views/payment/invoice-receipt.vue # src/views/payment/payment-application.vue # src/views/settlement/formal-settlement.vue # src/views/settlement/pre-settlement.vue
This commit is contained in:
@@ -271,7 +271,7 @@ export default {
|
||||
this.unwrapData(
|
||||
await billLedgerApi.getAvailableOptions(
|
||||
this.billDialog.keyword,
|
||||
this.form.deptId,
|
||||
undefined,
|
||||
this.form.billLedgerId
|
||||
)
|
||||
) || [];
|
||||
|
||||
@@ -251,10 +251,10 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不含税单价" min-width="130">
|
||||
<el-table-column label="合计金额" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.unitPriceNoTax"
|
||||
v-model="row.totalAmount"
|
||||
:disabled="readonly"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@@ -263,9 +263,6 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不含税金额" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.amountNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税率" min-width="110">
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
@@ -282,11 +279,14 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额" min-width="120">
|
||||
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
||||
<el-table-column label="不含税单价" min-width="140" align="right">
|
||||
<template #default="{ row }">{{ formatUnitPrice(row.unitPriceNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合计" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.totalAmount) }}</template>
|
||||
<el-table-column label="不含税金额" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.amountNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额" min-width="120" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="170">
|
||||
<template #default="{ row }">
|
||||
@@ -1101,15 +1101,30 @@ export default {
|
||||
addLine(sheet) {
|
||||
sheet.lines.push(emptyLine());
|
||||
},
|
||||
/**
|
||||
* 填写合计金额 + 税率后反算:
|
||||
* 合计金额 = 数量 × 不含税单价 × (1 + 税率)
|
||||
* 不含税单价 = 合计金额 / 数量 / (1 + 税率),保留 6 位小数
|
||||
* 不含税金额 = 数量 × 不含税单价,保留 2 位小数
|
||||
*/
|
||||
recalculateLine(row) {
|
||||
const quantity = Number(row.quantity || 0);
|
||||
const unitPrice = Number(row.unitPriceNoTax || 0);
|
||||
const amountNoTax = Number((quantity * unitPrice).toFixed(2));
|
||||
const totalAmount = Number(Number(row.totalAmount || 0).toFixed(2));
|
||||
const rate = Number(row.taxRate || 0) / 100;
|
||||
row.totalAmount = totalAmount;
|
||||
row.amountWithTax = totalAmount;
|
||||
if (quantity <= 0 || totalAmount <= 0) {
|
||||
row.unitPriceNoTax = 0;
|
||||
row.amountNoTax = 0;
|
||||
row.taxAmount = 0;
|
||||
return;
|
||||
}
|
||||
const factor = 1 + rate;
|
||||
const unitPriceNoTax = Number((totalAmount / quantity / factor).toFixed(6));
|
||||
const amountNoTax = Number((quantity * unitPriceNoTax).toFixed(2));
|
||||
row.unitPriceNoTax = unitPriceNoTax;
|
||||
row.amountNoTax = amountNoTax;
|
||||
row.taxAmount = rate ? Number((amountNoTax * rate).toFixed(2)) : 0;
|
||||
row.totalAmount = Number((amountNoTax + row.taxAmount).toFixed(2));
|
||||
row.amountWithTax = row.totalAmount;
|
||||
row.taxAmount = Number((totalAmount - amountNoTax).toFixed(2));
|
||||
},
|
||||
validateBusiness() {
|
||||
if (!this.selectedDetailIds.length) throw new Error('请至少选择一条开票明细');
|
||||
@@ -1122,12 +1137,14 @@ export default {
|
||||
for (const line of sheet.lines) {
|
||||
if (!line.goodsCategory || !line.goodsName) throw new Error('请完整填写商品和服务信息');
|
||||
if (
|
||||
[line.quantity, line.unitPriceNoTax, line.amountNoTax, line.taxRate].some(
|
||||
[line.quantity, line.totalAmount, line.unitPriceNoTax, line.amountNoTax, line.taxRate].some(
|
||||
Number.isNaN
|
||||
)
|
||||
) {
|
||||
throw new Error('开票商品行金额格式不正确');
|
||||
}
|
||||
if (Number(line.quantity || 0) <= 0) throw new Error('开票商品行数量必须大于0');
|
||||
if (Number(line.totalAmount || 0) <= 0) throw new Error('开票商品行合计金额必须大于0');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1227,6 +1244,9 @@ export default {
|
||||
formatMoney(value) {
|
||||
return Number(value || 0).toFixed(2);
|
||||
},
|
||||
formatUnitPrice(value) {
|
||||
return Number(value || 0).toFixed(6);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -270,6 +270,7 @@ export default {
|
||||
rows: [],
|
||||
selection: [],
|
||||
loading: false,
|
||||
approveLoading: false,
|
||||
batchSyncing: false,
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
mkEnabled: false,
|
||||
@@ -408,11 +409,17 @@ export default {
|
||||
});
|
||||
},
|
||||
async handleApprove(row) {
|
||||
await this.$confirm('确认审批通过该开票申请?', '提示', { type: 'warning' });
|
||||
// 防止重复点击:请求期间表格遮罩,后续点击直接忽略
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
// 防止重复点击:请求进行中忽略后续点击,避免重复审批和重复提示
|
||||
if (this.approveLoading) return;
|
||||
this.approveLoading = true;
|
||||
let loading = null;
|
||||
try {
|
||||
await this.$confirm('确认审批通过该开票申请?', '提示', { type: 'warning' });
|
||||
loading = this.$loading({
|
||||
lock: true,
|
||||
text: '审批处理中,请稍候…',
|
||||
background: 'rgba(255, 255, 255, 0.6)',
|
||||
});
|
||||
const res = await api.approve({ id: row.id });
|
||||
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||
if (res.data.data === false) {
|
||||
@@ -420,9 +427,10 @@ export default {
|
||||
} else {
|
||||
this.$message.success(res.data.msg || '审批通过');
|
||||
}
|
||||
await this.loadTable();
|
||||
this.loadTable();
|
||||
} finally {
|
||||
this.loading = false;
|
||||
loading?.close();
|
||||
this.approveLoading = false;
|
||||
}
|
||||
},
|
||||
async handleReturn(row) {
|
||||
|
||||
@@ -255,6 +255,7 @@ export default {
|
||||
rows: [],
|
||||
selection: [],
|
||||
loading: false,
|
||||
approveLoading: false,
|
||||
pushLoading: false,
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
mkEnabled: false,
|
||||
@@ -365,11 +366,17 @@ export default {
|
||||
});
|
||||
},
|
||||
async handleApprove(row) {
|
||||
await this.$confirm('确认审批通过该收票登记?', '提示', { type: 'warning' });
|
||||
// 防止重复点击:请求期间表格遮罩,后续点击直接忽略
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
// 防止重复点击:请求进行中忽略后续点击,避免重复审批和重复提示
|
||||
if (this.approveLoading) return;
|
||||
this.approveLoading = true;
|
||||
let loading = null;
|
||||
try {
|
||||
await this.$confirm('确认审批通过该收票登记?', '提示', { type: 'warning' });
|
||||
loading = this.$loading({
|
||||
lock: true,
|
||||
text: '审批处理中,请稍候…',
|
||||
background: 'rgba(255, 255, 255, 0.6)',
|
||||
});
|
||||
const res = await api.approve({ id: row.id });
|
||||
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||
if (res.data.data === false) {
|
||||
@@ -377,9 +384,10 @@ export default {
|
||||
} else {
|
||||
this.$message.success(res.data.msg || '审批通过');
|
||||
}
|
||||
await this.loadTable();
|
||||
this.loadTable();
|
||||
} finally {
|
||||
this.loading = false;
|
||||
loading?.close();
|
||||
this.approveLoading = false;
|
||||
}
|
||||
},
|
||||
async handleReturn(row) {
|
||||
|
||||
@@ -219,6 +219,11 @@
|
||||
show-word-limit /></el-form-item
|
||||
></el-col>
|
||||
</el-row>
|
||||
<!-- 风险客商提示(基本信息备注后,同合同新增页):收款方外部客商高风险/黑名单时展示,文案由后端 riskTip 拼装 -->
|
||||
<div v-if="payeeRiskText" class="payment-form-page__risk-tip">
|
||||
<div class="payment-form-page__risk-tip-title">风险客商提示:</div>
|
||||
<div class="payment-form-page__risk-tip-content">{{ payeeRiskText }}</div>
|
||||
</div>
|
||||
</section-card>
|
||||
<section-card v-if="isSettlementRelatedType" title="结算信息">
|
||||
<el-table :data="settlementRows" border
|
||||
@@ -244,7 +249,7 @@
|
||||
<el-table-column prop="paymentRatio" label="付款比例(%)" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-if="multipleSettlementRows && !readonly"
|
||||
v-if="!readonly"
|
||||
:model-value="row.paymentRatio"
|
||||
:min="0"
|
||||
:max="rowPaymentRatioMax(row)"
|
||||
@@ -262,7 +267,7 @@
|
||||
<el-table-column prop="appliedAmount" label="付款金额" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-if="multipleSettlementRows && !readonly"
|
||||
v-if="!readonly"
|
||||
:model-value="row.appliedAmount"
|
||||
:min="0"
|
||||
:max="Number(row.payableAmount || 0)"
|
||||
@@ -719,6 +724,7 @@ import * as billLedgerApi from '@/api/payment/billLedger';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
||||
import { getRisk as getCustomerRisk } from '@/api/business/hcs';
|
||||
import * as formalApi from '@/api/settlement/formalSettlement';
|
||||
import * as preApi from '@/api/settlement/preSettlement';
|
||||
import {
|
||||
@@ -874,6 +880,11 @@ export default {
|
||||
/** 合同付款比例设置(按第1笔、第2笔…顺序) */
|
||||
contractPaymentRatioRows: [],
|
||||
payeeCustomerLevel: '',
|
||||
// 收款方 C101-1 风险查询留档:risky=高风险/黑名单,tip=后端拼装提示文案
|
||||
payeeRisky: false,
|
||||
payeeRiskTip: '',
|
||||
payeeRiskLevel: '',
|
||||
payeeBlackStatus: '',
|
||||
attachmentRuleLoading: false,
|
||||
// 最近一次从结算单同步过来的附件 URL,切换结算单时用于整批替换
|
||||
linkedSettlementAttachmentUrls: [],
|
||||
@@ -1014,69 +1025,55 @@ export default {
|
||||
referenceSettlementCount() {
|
||||
return this.referenceSettlementNos.length;
|
||||
},
|
||||
multipleSettlementRows() {
|
||||
return this.referenceRows.length > 1;
|
||||
},
|
||||
// 结算单号展示:单张显示单号;多张仅提示数量,明细在下方「结算信息」表,悬停可看全部单号
|
||||
referenceDisplayText() {
|
||||
if (this.referenceSettlementCount > 1) return `已选 ${this.referenceSettlementCount} 张结算单`;
|
||||
return this.referenceLabel;
|
||||
},
|
||||
settlementRows() {
|
||||
if (this.referenceRows.length > 1) {
|
||||
// 多结算单:行内比例/金额独立维护,不回落到表头比例(表头与结算信息不关联)
|
||||
return this.referenceRows.map(item => {
|
||||
const paymentRatio =
|
||||
item.paymentRatio === undefined || item.paymentRatio === null || item.paymentRatio === ''
|
||||
? 0
|
||||
: Number(item.paymentRatio);
|
||||
const appliedAmount = Number(item.appliedAmount || 0);
|
||||
return {
|
||||
...item,
|
||||
paymentRatio,
|
||||
appliedAmount,
|
||||
...this.buildSettlementRatioHint({ ...item, paymentRatio }),
|
||||
};
|
||||
});
|
||||
}
|
||||
if (this.referenceRows.length) {
|
||||
return this.referenceRows.map(item => {
|
||||
const appliedAmount =
|
||||
item.fixedAppliedAmount === true
|
||||
? Number(item.appliedAmount || 0)
|
||||
: Number(
|
||||
(
|
||||
(Number(item.settlementAmount || 0) * Number(this.form.paymentRatio || 0)) /
|
||||
100
|
||||
).toFixed(2)
|
||||
);
|
||||
return {
|
||||
...item,
|
||||
paymentRatio: this.form.paymentRatio,
|
||||
appliedAmount,
|
||||
...this.buildSettlementRatioHint(item),
|
||||
};
|
||||
});
|
||||
}
|
||||
if (!this.referenceLabel) return [];
|
||||
return [
|
||||
{
|
||||
settlementNo: this.referenceLabel,
|
||||
settlementAmount: this.form.settlementAmount,
|
||||
payableAmount: this.form.payableAmount,
|
||||
paymentRatio: this.form.paymentRatio,
|
||||
appliedAmount: this.form.appliedAmount,
|
||||
paymentSequence: 1,
|
||||
...this.buildSettlementRatioHint({ paymentSequence: 1 }),
|
||||
},
|
||||
];
|
||||
const source = this.referenceRows.length
|
||||
? this.referenceRows
|
||||
: this.referenceLabel
|
||||
? [
|
||||
{
|
||||
settlementNo: this.referenceLabel,
|
||||
settlementAmount: this.form.settlementAmount,
|
||||
payableAmount: this.form.payableAmount,
|
||||
paymentRatio: null,
|
||||
appliedAmount: null,
|
||||
paymentSequence: 1,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
// 结算行比例/金额只取行内数据,不回落到付款基本信息
|
||||
return source.map(item => {
|
||||
const paymentRatio = this.nullableNumber(item.paymentRatio);
|
||||
const appliedAmount = this.nullableNumber(item.appliedAmount);
|
||||
return {
|
||||
...item,
|
||||
paymentRatio,
|
||||
appliedAmount,
|
||||
...this.buildSettlementRatioHint({ ...item, paymentRatio }),
|
||||
};
|
||||
});
|
||||
},
|
||||
highRiskPayee() {
|
||||
// 收款方 C101-1 高风险/黑名单,或客商等级 D/E/F(本地评级口径),命中须上传特批附件
|
||||
if (this.payeeRisky) return true;
|
||||
const level = String(this.payeeCustomerLevel || '')
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
return ['D', 'D级', 'E', 'E级', 'F', 'F级'].includes(level) || level.includes('高风险');
|
||||
},
|
||||
// 高风险/黑名单收款方提示(C101-1,同合同新增页风险客商提示口径;强制拦截在后端提交校验与金蝶推送门禁)
|
||||
payeeRiskText() {
|
||||
if (!this.payeeRisky) return '';
|
||||
const levelMap = { 0: '低风险', 1: '中风险', 2: '高风险' };
|
||||
const level = levelMap[this.payeeRiskLevel] || '未知';
|
||||
const blacklist = String(this.payeeBlackStatus || '').trim().toLowerCase() === 'yes' ? '是' : '否';
|
||||
const tip = this.payeeRiskTip || `收款方[${this.form.payeeName || ''}]为高风险/黑名单客商`;
|
||||
return `风险等级:${level}|黑名单:${blacklist}。${tip},按集团规定不可直接付款,提交前须上传「特批附件」类型附件,审批通过后将自动发起付款特批`;
|
||||
},
|
||||
requiredAttachmentMaterials() {
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
return [ATTACHMENT_MATERIALS.contract];
|
||||
@@ -1127,11 +1124,12 @@ export default {
|
||||
if (
|
||||
this.amountSyncing ||
|
||||
this.form.paymentType === 'project_advance' ||
|
||||
!this.paymentAmountBase
|
||||
!this.paymentAmountBase ||
|
||||
this.nullableNumber(this.form.paymentRatio) === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// 结算金额变化时按上方比例重算申请付款金额,仍不联动结算信息明细
|
||||
// 结算金额变化时只按上方比例重算申请付款金额,不改结算信息明细
|
||||
this.amountSyncing = true;
|
||||
this.form.appliedAmount = Number(
|
||||
((this.paymentAmountBase * Number(this.form.paymentRatio || 0)) / 100).toFixed(2)
|
||||
@@ -1229,14 +1227,6 @@ export default {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (
|
||||
selected.billType &&
|
||||
selected.billType !== 'received' &&
|
||||
!this.billMatchesSettlementParties(selected)
|
||||
) {
|
||||
callback(new Error('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票'));
|
||||
return;
|
||||
}
|
||||
if (selected.maturityDate) {
|
||||
const maturity = this.$dayjs(selected.maturityDate).startOf('day');
|
||||
const today = this.$dayjs().startOf('day');
|
||||
@@ -1540,8 +1530,6 @@ export default {
|
||||
paymentSequence: amount.paymentSequence,
|
||||
}),
|
||||
settlementNo: item.formalSettlementNo || item.settlementNo || '',
|
||||
appliedAmount: Number(item.appliedAmount || 0),
|
||||
fixedAppliedAmount: true,
|
||||
};
|
||||
});
|
||||
this.seedMultipleRowPayments(referenceRows);
|
||||
@@ -1553,27 +1541,6 @@ export default {
|
||||
(total, item) => total + Number(item.payableAmount || 0),
|
||||
0
|
||||
);
|
||||
const multiple = referenceRows.length > 1;
|
||||
// 多结算单:表头比例/申请金额独立;单结算单:沿用行金额或按比例计算
|
||||
const paymentRatio = Number(this.form.paymentRatio || 0);
|
||||
const rowsAppliedTotal = referenceRows.reduce(
|
||||
(total, item) => total + Number(item.appliedAmount || 0),
|
||||
0
|
||||
);
|
||||
const headerAppliedAmount = multiple
|
||||
? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
|
||||
: Number(
|
||||
(
|
||||
rowsAppliedTotal > 0
|
||||
? rowsAppliedTotal
|
||||
: (payableAmount * paymentRatio) / 100
|
||||
).toFixed(2)
|
||||
);
|
||||
const headerPaymentRatio = multiple
|
||||
? paymentRatio
|
||||
: settlementAmount
|
||||
? Number(((headerAppliedAmount / settlementAmount) * 100).toFixed(2))
|
||||
: paymentRatio;
|
||||
const settlementIds = rows.map(item => item.settlementId || item.id).filter(Boolean);
|
||||
this.amountSyncing = true;
|
||||
this.referenceRows = referenceRows;
|
||||
@@ -1600,8 +1567,7 @@ export default {
|
||||
settlementAmount: Number(settlementAmount.toFixed(2)),
|
||||
payableAmount: Number(payableAmount.toFixed(2)),
|
||||
billType: '',
|
||||
paymentRatio: headerPaymentRatio,
|
||||
appliedAmount: headerAppliedAmount,
|
||||
appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
|
||||
invoices: rows.flatMap(item =>
|
||||
this.createInvoiceRows(item.invoices || [], item.formalSettlementNo || item.settlementNo)
|
||||
),
|
||||
@@ -1657,7 +1623,7 @@ export default {
|
||||
referenceType: isPre ? 'pre' : 'formal',
|
||||
settlementAmount: Number(Number(item.settlementAmount || 0).toFixed(2)),
|
||||
payableAmount: Number(Number(item.payableAmount ?? item.settlementAmount ?? 0).toFixed(2)),
|
||||
appliedAmount: Number(Number(item.appliedAmount || 0).toFixed(2)),
|
||||
appliedAmount: this.nullableNumber(item.appliedAmount),
|
||||
paymentRatio: Number(item.settlementAmount || 0)
|
||||
? Number(
|
||||
(
|
||||
@@ -1666,11 +1632,10 @@ export default {
|
||||
).toFixed(2)
|
||||
)
|
||||
: null,
|
||||
fixedAppliedAmount: this.readonly,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
if (rows.length > 1) {
|
||||
if (rows.length) {
|
||||
this.form.preSettlementIds = rows.filter(item => item.referenceType === 'pre').map(item => item.id);
|
||||
this.form.settlementIds = rows.filter(item => item.referenceType === 'formal').map(item => item.id);
|
||||
if (this.form.preSettlementIds.length) this.form.preSettlementId = this.form.preSettlementIds[0];
|
||||
@@ -1704,18 +1669,27 @@ export default {
|
||||
settlementAmount: Number(Number(settlementAmount || 0).toFixed(2)),
|
||||
payableAmount: Number(Math.max(0, payableAmount).toFixed(2)),
|
||||
paymentSequence: Number.isFinite(paymentSequence) && paymentSequence > 0 ? paymentSequence : 1,
|
||||
paymentRatio: null,
|
||||
appliedAmount: null,
|
||||
};
|
||||
},
|
||||
nullableNumber(value) {
|
||||
if (value === undefined || value === null || value === '') return null;
|
||||
const amount = Number(value);
|
||||
return Number.isFinite(amount) ? amount : null;
|
||||
},
|
||||
/** 付款基本信息内部:已填写比例时,按结算金额重算申请付款金额;不读取结算行付款金额。 */
|
||||
headerAppliedAmountByRatio(settlementAmount) {
|
||||
if (this.nullableNumber(this.form.paymentRatio) === null) return this.form.appliedAmount;
|
||||
return Number(((Number(settlementAmount || 0) * Number(this.form.paymentRatio)) / 100).toFixed(2));
|
||||
},
|
||||
/**
|
||||
* 按合同付款比例配置,判断当前付款次序是否超上限。
|
||||
* @param {Object} row 结算信息行(含 paymentSequence)
|
||||
*/
|
||||
buildSettlementRatioHint(row = {}) {
|
||||
const sequence = Number(row.paymentSequence || 1);
|
||||
const rawRatio =
|
||||
row.paymentRatio !== undefined && row.paymentRatio !== null && row.paymentRatio !== ''
|
||||
? row.paymentRatio
|
||||
: this.form.paymentRatio;
|
||||
const rawRatio = row.paymentRatio;
|
||||
const paymentRatio = Number(rawRatio);
|
||||
const ratioConfig = this.contractPaymentRatioRows[sequence - 1];
|
||||
const ratioLimitValue = ratioConfig?.ratioLimit;
|
||||
@@ -1858,21 +1832,6 @@ export default {
|
||||
(total, item) => total + Number(item.payableAmount || 0),
|
||||
0
|
||||
);
|
||||
const multiple = this.referenceRows.length > 1;
|
||||
const paymentRatio = Number(this.form.paymentRatio || 0);
|
||||
const rowsAppliedTotal = this.referenceRows.reduce(
|
||||
(total, item) => total + Number(item.appliedAmount || 0),
|
||||
0
|
||||
);
|
||||
// 多结算单:表头申请金额只由上方比例计算;单结算单仍按可付款金额×比例
|
||||
const headerAppliedAmount = multiple
|
||||
? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
|
||||
: Number(((payableAmount * paymentRatio) / 100).toFixed(2));
|
||||
const headerPaymentRatio = multiple
|
||||
? paymentRatio
|
||||
: settlementAmount && rowsAppliedTotal > 0
|
||||
? Number(((rowsAppliedTotal / settlementAmount) * 100).toFixed(2))
|
||||
: paymentRatio;
|
||||
// 尾款付款场景下选择预付结算单时保留尾款付款类型,不强制改为进度预付
|
||||
this.amountSyncing = true;
|
||||
Object.assign(this.form, {
|
||||
@@ -1898,8 +1857,7 @@ export default {
|
||||
settlementAmount: Number(settlementAmount.toFixed(2)),
|
||||
payableAmount: Number(payableAmount.toFixed(2)),
|
||||
billType: '',
|
||||
paymentRatio: headerPaymentRatio,
|
||||
appliedAmount: headerAppliedAmount,
|
||||
appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
|
||||
});
|
||||
this.rememberSettlementParties(first.payerName, first.payeeName);
|
||||
this.paymentRecords = [];
|
||||
@@ -2381,6 +2339,10 @@ export default {
|
||||
const preserveCurrentValue = autoSelectDefault === false;
|
||||
this.receiptAccountOptions = [];
|
||||
this.payeeCustomerLevel = '';
|
||||
this.payeeRisky = false;
|
||||
this.payeeRiskTip = '';
|
||||
this.payeeRiskLevel = '';
|
||||
this.payeeBlackStatus = '';
|
||||
if (!partyBName) {
|
||||
if (!preserveCurrentValue) this.clearReceiptAccount();
|
||||
return;
|
||||
@@ -2412,6 +2374,19 @@ export default {
|
||||
}
|
||||
const detail = this.unwrapData(await getCustomerArchiveDetail(customer.id));
|
||||
this.payeeCustomerLevel = detail.customerLevel || '';
|
||||
// 收款方实时查 C101-1 客商风险(后端对内部组织客商默认返回无风险):高风险/黑名单在
|
||||
// 基本信息"风险客商提示"条常驻展示并要求特批附件;查询失败不阻断填写,提交时后端强校验兜底
|
||||
if (detail.customerCode) {
|
||||
try {
|
||||
const risk = this.unwrapData(await getCustomerRisk(detail.customerCode)) || {};
|
||||
this.payeeRisky = Boolean(risk.risky);
|
||||
this.payeeRiskTip = risk.riskTip || '';
|
||||
this.payeeRiskLevel = risk.fdRiskLevel || '';
|
||||
this.payeeBlackStatus = risk.fdBlackStatus || '';
|
||||
} catch (error) {
|
||||
// 忽略风险查询异常,提交时后端 C101-1 强校验兜底
|
||||
}
|
||||
}
|
||||
this.receiptAccountOptions = (detail.receiptAccounts || [])
|
||||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount)
|
||||
.map(item => ({
|
||||
@@ -2444,8 +2419,7 @@ export default {
|
||||
}
|
||||
},
|
||||
payeeReceiptLabel(item) {
|
||||
const name = item.accountName || item.accountHolderName || '未命名收款方';
|
||||
return item.bankAccount ? `${name}(${item.bankAccount})` : name;
|
||||
return item.accountName || item.accountHolderName || '未命名收款方';
|
||||
},
|
||||
receiptAccountLabel(item) {
|
||||
return [item.accountName || item.accountHolderName, item.bankName, item.bankAccount]
|
||||
@@ -2603,7 +2577,7 @@ export default {
|
||||
this.unwrapData(
|
||||
await billLedgerApi.getAvailableOptions(
|
||||
keyword,
|
||||
this.form.deptId,
|
||||
undefined,
|
||||
selectedId,
|
||||
this.settlementPayerName,
|
||||
this.settlementPayeeName
|
||||
@@ -2628,7 +2602,7 @@ export default {
|
||||
this.billPage.current,
|
||||
this.billPage.size,
|
||||
String(this.billQuery.keyword || '').trim() || undefined,
|
||||
this.form.deptId,
|
||||
undefined,
|
||||
this.form.billLedgerId,
|
||||
this.settlementPayerName,
|
||||
this.settlementPayeeName
|
||||
@@ -2667,14 +2641,6 @@ export default {
|
||||
);
|
||||
},
|
||||
selectBill(row) {
|
||||
if (
|
||||
row?.billType &&
|
||||
row.billType !== 'received' &&
|
||||
!this.billMatchesSettlementParties(row)
|
||||
) {
|
||||
ElMessage.warning('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票');
|
||||
return;
|
||||
}
|
||||
if (row?.maturityDate) {
|
||||
const maturity = this.$dayjs(row.maturityDate).startOf('day');
|
||||
const today = this.$dayjs().startOf('day');
|
||||
@@ -3097,22 +3063,54 @@ export default {
|
||||
return Number(Math.min(100, (payableAmount / settlementAmount) * 100).toFixed(2));
|
||||
},
|
||||
/**
|
||||
* 多结算单行金额规范化:仅在行内反算比例,不使用表头比例灌入(表头与结算信息不关联)。
|
||||
* 结算行金额规范化:只在行内反算比例,不使用付款基本信息的比例和申请金额。
|
||||
*/
|
||||
seedMultipleRowPayments(rows = []) {
|
||||
if (rows.length <= 1) return;
|
||||
rows.forEach(item => {
|
||||
const settlementAmount = Number(item.settlementAmount || 0);
|
||||
const payableAmount = Number(item.payableAmount || 0);
|
||||
let applied = Number(item.appliedAmount || 0);
|
||||
if (!Number.isFinite(applied) || applied < 0) applied = 0;
|
||||
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
|
||||
item.appliedAmount = Number(applied.toFixed(2));
|
||||
item.paymentRatio = settlementAmount
|
||||
? Number(((item.appliedAmount / settlementAmount) * 100).toFixed(2))
|
||||
: 0;
|
||||
if (this.nullableNumber(item.appliedAmount) === null && this.nullableNumber(item.paymentRatio) === null) {
|
||||
item.appliedAmount = null;
|
||||
item.paymentRatio = null;
|
||||
return;
|
||||
}
|
||||
this.syncRowPayment(item, 'amount');
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 结算行付款比例与付款金额联动。比例 = 付款金额 / 结算总金额。
|
||||
* @param {'ratio'|'amount'} source 以哪一侧为准
|
||||
*/
|
||||
syncRowPayment(row, source) {
|
||||
const settlementAmount = Number(row.settlementAmount || 0);
|
||||
const payableAmount = Number(row.payableAmount || 0);
|
||||
if (source === 'ratio') {
|
||||
const ratio = this.nullableNumber(row.paymentRatio);
|
||||
if (ratio === null) {
|
||||
row.paymentRatio = null;
|
||||
row.appliedAmount = null;
|
||||
return;
|
||||
}
|
||||
let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
|
||||
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
|
||||
row.appliedAmount = applied;
|
||||
row.paymentRatio = settlementAmount
|
||||
? Number(((applied / settlementAmount) * 100).toFixed(2))
|
||||
: Number(ratio.toFixed(2));
|
||||
return;
|
||||
}
|
||||
const appliedValue = this.nullableNumber(row.appliedAmount);
|
||||
if (appliedValue === null) {
|
||||
row.appliedAmount = null;
|
||||
row.paymentRatio = null;
|
||||
return;
|
||||
}
|
||||
let applied = appliedValue;
|
||||
if (applied < 0) applied = 0;
|
||||
if (applied > payableAmount) applied = payableAmount;
|
||||
row.appliedAmount = Number(applied.toFixed(2));
|
||||
row.paymentRatio = settlementAmount
|
||||
? Number(((row.appliedAmount / settlementAmount) * 100).toFixed(2))
|
||||
: 0;
|
||||
},
|
||||
findReferenceRow(row) {
|
||||
return this.referenceRows.find(
|
||||
item =>
|
||||
@@ -3121,55 +3119,19 @@ export default {
|
||||
item.settlementNo === row.settlementNo
|
||||
);
|
||||
},
|
||||
/** 结算信息付款金额合计 */
|
||||
settlementRowsAppliedTotal(rows = this.referenceRows) {
|
||||
return (rows || []).reduce((total, item) => total + Number(item.appliedAmount || 0), 0);
|
||||
},
|
||||
/** 结算信息合计不得超过上方申请付款金额 */
|
||||
ensureSettlementAppliedWithinHeader(showWarning = true) {
|
||||
if (!this.multipleSettlementRows) return true;
|
||||
const headerApplied = Number(this.form.appliedAmount || 0);
|
||||
const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
|
||||
if (rowsTotal - headerApplied > 1e-8) {
|
||||
if (showWarning) {
|
||||
this.$message.warning(
|
||||
`结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${headerApplied.toFixed(2)})`
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleRowPaymentRatioChange(row, value) {
|
||||
const target = this.findReferenceRow(row);
|
||||
if (!target) return;
|
||||
const settlementAmount = Number(target.settlementAmount || 0);
|
||||
const payableAmount = Number(target.payableAmount || 0);
|
||||
const ratio = value === null || value === undefined || value === '' ? 0 : Number(value);
|
||||
let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
|
||||
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
|
||||
target.appliedAmount = applied;
|
||||
target.paymentRatio = settlementAmount
|
||||
? Number(((applied / settlementAmount) * 100).toFixed(2))
|
||||
: Number(ratio.toFixed(2));
|
||||
// 仅刷新结算金额/可付款金额合计,不回写表头申请金额与比例
|
||||
target.paymentRatio = value;
|
||||
this.syncRowPayment(target, 'ratio');
|
||||
this.syncHeaderSettlementTotalsFromRows();
|
||||
this.ensureSettlementAppliedWithinHeader(true);
|
||||
},
|
||||
handleRowAppliedAmountChange(row, value) {
|
||||
const target = this.findReferenceRow(row);
|
||||
if (!target) return;
|
||||
const settlementAmount = Number(target.settlementAmount || 0);
|
||||
const payableAmount = Number(target.payableAmount || 0);
|
||||
let applied = value === null || value === undefined || value === '' ? 0 : Number(value);
|
||||
if (!Number.isFinite(applied) || applied < 0) applied = 0;
|
||||
if (applied > payableAmount) applied = payableAmount;
|
||||
target.appliedAmount = Number(applied.toFixed(2));
|
||||
target.paymentRatio = settlementAmount
|
||||
? Number(((target.appliedAmount / settlementAmount) * 100).toFixed(2))
|
||||
: 0;
|
||||
target.appliedAmount = value;
|
||||
this.syncRowPayment(target, 'amount');
|
||||
this.syncHeaderSettlementTotalsFromRows();
|
||||
this.ensureSettlementAppliedWithinHeader(true);
|
||||
},
|
||||
/** 多结算单时仅同步结算金额/可付款金额合计,申请付款金额与比例由上方独立维护 */
|
||||
syncHeaderSettlementTotalsFromRows() {
|
||||
@@ -3278,8 +3240,8 @@ export default {
|
||||
settlementLines: (this.referenceRows || []).map(item => ({
|
||||
formalSettlementId: item.referenceType === 'formal' ? item.id : null,
|
||||
preSettlementId: item.referenceType === 'pre' ? item.id : null,
|
||||
paymentRatio: item.paymentRatio,
|
||||
appliedAmount: item.appliedAmount,
|
||||
paymentRatio: this.nullableNumber(item.paymentRatio),
|
||||
appliedAmount: this.nullableNumber(item.appliedAmount),
|
||||
})),
|
||||
paymentMethod,
|
||||
billLedgerId,
|
||||
@@ -3340,21 +3302,22 @@ export default {
|
||||
ElMessage.warning('申请付款金额必填且必须大于0');
|
||||
return false;
|
||||
}
|
||||
if (this.multipleSettlementRows) {
|
||||
const invalidRow = this.referenceRows.find(
|
||||
item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
|
||||
);
|
||||
if (this.isSettlementRelatedType && this.referenceRows.length) {
|
||||
const invalidRow = this.referenceRows.find(item => {
|
||||
const rowApplied = Number(item.appliedAmount || 0);
|
||||
return !Number.isFinite(rowApplied) || rowApplied <= 0;
|
||||
});
|
||||
if (invalidRow) {
|
||||
this.clearMessages();
|
||||
ElMessage.warning(`${invalidRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
|
||||
ElMessage.warning(`${invalidRow.settlementNo || '结算单'}请填写付款金额,且必须大于0`);
|
||||
return false;
|
||||
}
|
||||
const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
|
||||
if (rowsTotal - appliedAmount > 1e-8) {
|
||||
const exceededRow = this.referenceRows.find(
|
||||
item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
|
||||
);
|
||||
if (exceededRow) {
|
||||
this.clearMessages();
|
||||
ElMessage.warning(
|
||||
`结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${appliedAmount.toFixed(2)})`
|
||||
);
|
||||
ElMessage.warning(`${exceededRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3388,6 +3351,15 @@ export default {
|
||||
this.clearMessages();
|
||||
const saved = await this.saveDraft({ showSuccess: false, closePage: false });
|
||||
if (!saved) return;
|
||||
// 高风险/黑名单收款方必须上传特批附件(与后端提交强校验同口径)
|
||||
if (
|
||||
this.missingAttachmentMaterials.some(
|
||||
item => item.key === ATTACHMENT_MATERIALS.specialApproval.key
|
||||
)
|
||||
) {
|
||||
this.$message.warning('收款方为高风险/黑名单客商,必须上传「特批附件」类型附件后再提交审批');
|
||||
return;
|
||||
}
|
||||
const previousApprovalStatus = this.form.approvalStatus || '';
|
||||
await api.submit({ id: this.form.id });
|
||||
await submitMkApprovalFlow({
|
||||
@@ -3420,6 +3392,26 @@ export default {
|
||||
.payment-form-page__form {
|
||||
padding-bottom: 72px;
|
||||
}
|
||||
|
||||
// 风险客商提示条(基本信息备注后,同合同新增页):橙黄背景 + 红色文字
|
||||
.payment-form-page__risk-tip {
|
||||
margin: 0 0 18px;
|
||||
padding: 10px 16px;
|
||||
background: #faecd8;
|
||||
border-radius: 4px;
|
||||
|
||||
.payment-form-page__risk-tip-title {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.payment-form-page__risk-tip-content {
|
||||
color: #f56c6c;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-form-page__settlement-nos {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -163,6 +163,7 @@ export default {
|
||||
rows: [],
|
||||
selection: [],
|
||||
loading: false,
|
||||
approveLoading: false,
|
||||
syncLoading: false,
|
||||
resultSyncing: false,
|
||||
query: {},
|
||||
@@ -327,9 +328,14 @@ export default {
|
||||
this.refreshChange();
|
||||
},
|
||||
async handleApprove(row) {
|
||||
// 防止重复点击:请求期间表格遮罩,后续点击直接忽略
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
// 防止重复点击:请求进行中忽略后续点击
|
||||
if (this.approveLoading) return;
|
||||
this.approveLoading = true;
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: '审批处理中,请稍候…',
|
||||
background: 'rgba(255, 255, 255, 0.6)',
|
||||
});
|
||||
try {
|
||||
const res = await api.approve({ id: row.id });
|
||||
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||
@@ -338,9 +344,10 @@ export default {
|
||||
} else {
|
||||
this.$message.success(res.data.msg || '审批通过');
|
||||
}
|
||||
await this.onLoad(this.page);
|
||||
this.refreshChange();
|
||||
} finally {
|
||||
this.loading = false;
|
||||
loading.close();
|
||||
this.approveLoading = false;
|
||||
}
|
||||
},
|
||||
handleMkAudit(row) {
|
||||
|
||||
Reference in New Issue
Block a user