@@ -8,8 +8,20 @@ export const preSettlementFormFields = [
|
|||||||
{ label: '合同名称', prop: 'contractId', type: 'contract', required: true },
|
{ label: '合同名称', prop: 'contractId', type: 'contract', required: true },
|
||||||
{ label: '合同编号', prop: 'contractNo', readonly: true },
|
{ label: '合同编号', prop: 'contractNo', readonly: true },
|
||||||
{ label: '项目名称', prop: 'projectName', readonly: true },
|
{ label: '项目名称', prop: 'projectName', readonly: true },
|
||||||
{ label: '收款方', prop: 'payeeName', readonly: true },
|
{
|
||||||
{ label: '付款方', prop: 'payerName', readonly: true },
|
label: '收款方',
|
||||||
|
prop: 'payeeName',
|
||||||
|
type: 'select',
|
||||||
|
optionsKey: 'payeeOptions',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '付款方',
|
||||||
|
prop: 'payerName',
|
||||||
|
type: 'select',
|
||||||
|
optionsKey: 'payerOptions',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
{ label: '结算类型', prop: 'settlementType', readonly: true },
|
{ label: '结算类型', prop: 'settlementType', readonly: true },
|
||||||
{ label: '结算金额', prop: 'settlementAmount', readonly: true, money: true },
|
{ label: '结算金额', prop: 'settlementAmount', readonly: true, money: true },
|
||||||
{ label: '业务模式', prop: 'businessModel', type: 'select', optionsKey: 'businessModelOptions' },
|
{ label: '业务模式', prop: 'businessModel', type: 'select', optionsKey: 'businessModelOptions' },
|
||||||
@@ -24,6 +36,8 @@ export const preSettlementFormFields = [
|
|||||||
|
|
||||||
export const preSettlementFormRules = {
|
export const preSettlementFormRules = {
|
||||||
contractId: [{ required: true, message: '请选择合同名称', trigger: 'change' }],
|
contractId: [{ required: true, message: '请选择合同名称', trigger: 'change' }],
|
||||||
|
payeeName: [{ required: true, message: '请选择收款方', trigger: 'change' }],
|
||||||
|
payerName: [{ required: true, message: '请选择付款方', trigger: 'change' }],
|
||||||
exchangeRateDate: [
|
exchangeRateDate: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback, source) => {
|
validator: (_rule, value, callback, source) => {
|
||||||
|
|||||||
@@ -989,6 +989,7 @@ import {
|
|||||||
save,
|
save,
|
||||||
submit,
|
submit,
|
||||||
} from '@/api/settlement/preSettlement';
|
} from '@/api/settlement/preSettlement';
|
||||||
|
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
||||||
import { calculateAdjustedFee, getFeeDetail } from '@/api/settlement/receivable-payable-detail';
|
import { calculateAdjustedFee, getFeeDetail } from '@/api/settlement/receivable-payable-detail';
|
||||||
import {
|
import {
|
||||||
resolveReceivablePayableDetailId,
|
resolveReceivablePayableDetailId,
|
||||||
@@ -1072,6 +1073,8 @@ export default {
|
|||||||
changeTableColumns: changeRecordColumns,
|
changeTableColumns: changeRecordColumns,
|
||||||
candidateColumns: candidateDetailColumns,
|
candidateColumns: candidateDetailColumns,
|
||||||
contractOptions: [],
|
contractOptions: [],
|
||||||
|
payerOptions: [],
|
||||||
|
payeeOptions: [],
|
||||||
contractCategoryOptions,
|
contractCategoryOptions,
|
||||||
signTypeOptions,
|
signTypeOptions,
|
||||||
effectiveTypeOptions,
|
effectiveTypeOptions,
|
||||||
@@ -1391,18 +1394,34 @@ export default {
|
|||||||
const first = rows[0];
|
const first = rows[0];
|
||||||
const settlementType = this.initialData.settlementType || first.settlementType || 'payable';
|
const settlementType = this.initialData.settlementType || first.settlementType || 'payable';
|
||||||
const contractId = first.contractId || '';
|
const contractId = first.contractId || '';
|
||||||
let contractPartyA = first.payerName || '';
|
let contractPartyA = '';
|
||||||
let contractPartyB = first.payeeName || '';
|
let contractPartyB = '';
|
||||||
if (contractId && (!contractPartyA || !contractPartyB)) {
|
let contractPartyAJson = '';
|
||||||
|
let contractPartyBJson = '';
|
||||||
|
if (contractId) {
|
||||||
try {
|
try {
|
||||||
const { data } = await getContractOptions(first.contractNo || '');
|
const contractDetail = await this.fetchContractPartyDetail(contractId);
|
||||||
const matched = (data?.data || []).find(item => String(item.id) === String(contractId));
|
if (contractDetail) {
|
||||||
contractPartyA = contractPartyA || matched?.partyA || '';
|
contractPartyA = contractDetail.partyA || '';
|
||||||
contractPartyB = contractPartyB || matched?.partyB || '';
|
contractPartyB = contractDetail.partyB || '';
|
||||||
|
contractPartyAJson = contractDetail.partyAJson || '';
|
||||||
|
contractPartyBJson = contractDetail.partyBJson || '';
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
contractPartyA = '';
|
contractPartyA = '';
|
||||||
contractPartyB = '';
|
contractPartyB = '';
|
||||||
}
|
}
|
||||||
|
if (!contractPartyA && !contractPartyB) {
|
||||||
|
try {
|
||||||
|
const { data } = await getContractOptions(first.contractNo || '');
|
||||||
|
const matched = (data?.data || []).find(item => String(item.id) === String(contractId));
|
||||||
|
contractPartyA = matched?.partyA || '';
|
||||||
|
contractPartyB = matched?.partyB || '';
|
||||||
|
} catch (error) {
|
||||||
|
contractPartyA = first.payerName || '';
|
||||||
|
contractPartyB = first.payeeName || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!this.contractOptions.some(item => String(item.id) === String(contractId))) {
|
if (!this.contractOptions.some(item => String(item.id) === String(contractId))) {
|
||||||
this.contractOptions.push({
|
this.contractOptions.push({
|
||||||
@@ -1416,6 +1435,8 @@ export default {
|
|||||||
settlementType,
|
settlementType,
|
||||||
partyA: contractPartyA,
|
partyA: contractPartyA,
|
||||||
partyB: contractPartyB,
|
partyB: contractPartyB,
|
||||||
|
partyAJson: contractPartyAJson,
|
||||||
|
partyBJson: contractPartyBJson,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.handleContractChange(contractId);
|
this.handleContractChange(contractId);
|
||||||
@@ -1427,12 +1448,12 @@ export default {
|
|||||||
projectName: first.projectName || this.form.projectName,
|
projectName: first.projectName || this.form.projectName,
|
||||||
deptId: first.deptId || this.form.deptId,
|
deptId: first.deptId || this.form.deptId,
|
||||||
deptName: first.deptName || this.form.deptName,
|
deptName: first.deptName || this.form.deptName,
|
||||||
payerName: this.form.payerName || contractPartyA,
|
|
||||||
payeeName: this.form.payeeName || contractPartyB,
|
|
||||||
settlementType,
|
settlementType,
|
||||||
currency: first.currency || 'RMB',
|
currency: first.currency || 'RMB',
|
||||||
localCurrency: first.localCurrency || 'RMB',
|
localCurrency: first.localCurrency || 'RMB',
|
||||||
});
|
});
|
||||||
|
// 明细带入的收付方若为单值且在选项中,优先保留;多方拼接值由下拉默认首项承接
|
||||||
|
this.applyPartySelection(first.payerName, first.payeeName);
|
||||||
this.details = rows.map(row => this.normalizeSourceDetail(row));
|
this.details = rows.map(row => this.normalizeSourceDetail(row));
|
||||||
this.detailPage.current = 1;
|
this.detailPage.current = 1;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@@ -1516,6 +1537,8 @@ export default {
|
|||||||
this.billingRuleDialog.visible = false;
|
this.billingRuleDialog.visible = false;
|
||||||
this.billingRuleDialog.rule = null;
|
this.billingRuleDialog.rule = null;
|
||||||
this.contractOptions = [];
|
this.contractOptions = [];
|
||||||
|
this.payerOptions = [];
|
||||||
|
this.payeeOptions = [];
|
||||||
this.contractDialog.visible = false;
|
this.contractDialog.visible = false;
|
||||||
this.contractDialog.expanded = false;
|
this.contractDialog.expanded = false;
|
||||||
this.contractDialog.rows = [];
|
this.contractDialog.rows = [];
|
||||||
@@ -1552,21 +1575,20 @@ export default {
|
|||||||
this.attachments = this.parseAttachments(detail.attachmentsJson);
|
this.attachments = this.parseAttachments(detail.attachmentsJson);
|
||||||
this.sortAttachments();
|
this.sortAttachments();
|
||||||
this.selectedAttachmentRows = [];
|
this.selectedAttachmentRows = [];
|
||||||
if (
|
if (detail.contractId) {
|
||||||
detail.contractId &&
|
await this.ensureContractPartyOptions(detail.contractId, {
|
||||||
!this.contractOptions.some(item => item.id === detail.contractId)
|
|
||||||
) {
|
|
||||||
this.contractOptions.push({
|
|
||||||
id: detail.contractId,
|
|
||||||
contractNo: detail.contractNo,
|
contractNo: detail.contractNo,
|
||||||
contractName: detail.contractName,
|
contractName: detail.contractName,
|
||||||
projectId: detail.projectId,
|
projectId: detail.projectId,
|
||||||
projectName: detail.projectName,
|
projectName: detail.projectName,
|
||||||
deptId: detail.deptId,
|
deptId: detail.deptId,
|
||||||
deptName: detail.deptName,
|
deptName: detail.deptName,
|
||||||
partyA: detail.payerName,
|
settlementType: detail.settlementType,
|
||||||
partyB: detail.payeeName,
|
|
||||||
});
|
});
|
||||||
|
this.applyPartySelection(detail.payerName, detail.payeeName);
|
||||||
|
} else {
|
||||||
|
this.payerOptions = [];
|
||||||
|
this.payeeOptions = [];
|
||||||
}
|
}
|
||||||
if (this.publicMode && detail.detailFees) {
|
if (this.publicMode && detail.detailFees) {
|
||||||
Object.entries(detail.detailFees).forEach(([detailId, feeRows]) => {
|
Object.entries(detail.detailFees).forEach(([detailId, feeRows]) => {
|
||||||
@@ -1640,6 +1662,8 @@ export default {
|
|||||||
deptName: row.deptName || row.organizationName,
|
deptName: row.deptName || row.organizationName,
|
||||||
partyA: row.partyA || row.payerName,
|
partyA: row.partyA || row.payerName,
|
||||||
partyB: row.partyB || row.payeeName,
|
partyB: row.partyB || row.payeeName,
|
||||||
|
partyAJson: row.partyAJson || '',
|
||||||
|
partyBJson: row.partyBJson || '',
|
||||||
settlementType:
|
settlementType:
|
||||||
row.settlementType || (row.contractCategory === '客户合同' ? 'receivable' : 'payable'),
|
row.settlementType || (row.contractCategory === '客户合同' ? 'receivable' : 'payable'),
|
||||||
};
|
};
|
||||||
@@ -1649,7 +1673,7 @@ export default {
|
|||||||
this.handleContractChange(contract.id);
|
this.handleContractChange(contract.id);
|
||||||
this.contractDialog.visible = false;
|
this.contractDialog.visible = false;
|
||||||
this.contractDialog.current = null;
|
this.contractDialog.current = null;
|
||||||
this.$refs.formRef?.clearValidate('contractId');
|
this.$refs.formRef?.clearValidate(['contractId', 'payerName', 'payeeName']);
|
||||||
},
|
},
|
||||||
contractCategoryName(value) {
|
contractCategoryName(value) {
|
||||||
return (
|
return (
|
||||||
@@ -1714,6 +1738,8 @@ export default {
|
|||||||
this.form.deptName = '';
|
this.form.deptName = '';
|
||||||
this.form.payerName = '';
|
this.form.payerName = '';
|
||||||
this.form.payeeName = '';
|
this.form.payeeName = '';
|
||||||
|
this.payerOptions = [];
|
||||||
|
this.payeeOptions = [];
|
||||||
this.summaryFees = [];
|
this.summaryFees = [];
|
||||||
this.projectAdvanceWriteOffs = [];
|
this.projectAdvanceWriteOffs = [];
|
||||||
return;
|
return;
|
||||||
@@ -1726,9 +1752,8 @@ export default {
|
|||||||
this.form.deptId = contract.deptId;
|
this.form.deptId = contract.deptId;
|
||||||
this.form.deptName = contract.deptName;
|
this.form.deptName = contract.deptName;
|
||||||
this.form.settlementType = contract.settlementType || 'payable';
|
this.form.settlementType = contract.settlementType || 'payable';
|
||||||
// 部分候选合同只带 id/名称,缺甲方乙方;此时保留已带出的收付方,不要清成 undefined
|
this.syncPartyOptionsFromContract(contract);
|
||||||
if (contract.partyA) this.form.payerName = contract.partyA;
|
this.applyPartySelection('', '');
|
||||||
if (contract.partyB) this.form.payeeName = contract.partyB;
|
|
||||||
this.form.currency = contract.settlementCurrency || 'RMB';
|
this.form.currency = contract.settlementCurrency || 'RMB';
|
||||||
this.summaryFees = [];
|
this.summaryFees = [];
|
||||||
this.projectAdvanceWriteOffs = [];
|
this.projectAdvanceWriteOffs = [];
|
||||||
@@ -1737,6 +1762,152 @@ export default {
|
|||||||
this.fetchBfiExchangeRate();
|
this.fetchBfiExchangeRate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
parsePartyNames(partyJson, fallbackName) {
|
||||||
|
try {
|
||||||
|
const parsed = partyJson ? JSON.parse(partyJson) : [];
|
||||||
|
if (Array.isArray(parsed) && parsed.length) {
|
||||||
|
const names = parsed
|
||||||
|
.map(item => String(item?.customerName || '').trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (names.length) return [...new Set(names)];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// JSON 解析失败时回退到名称拆分
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
...new Set(
|
||||||
|
String(fallbackName || '')
|
||||||
|
.split(/[、,,]/)
|
||||||
|
.map(name => name.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
buildPartySelectOptions(names) {
|
||||||
|
return (names || []).map(name => ({ dictKey: name, dictValue: name }));
|
||||||
|
},
|
||||||
|
resolveContractPartyNames(contract) {
|
||||||
|
const settlementType = contract?.settlementType || this.form.settlementType || 'payable';
|
||||||
|
const partyANames = this.parsePartyNames(contract?.partyAJson, contract?.partyA);
|
||||||
|
const partyBNames = this.parsePartyNames(contract?.partyBJson, contract?.partyB);
|
||||||
|
if (settlementType === 'receivable') {
|
||||||
|
return { payerNames: partyBNames, payeeNames: partyANames };
|
||||||
|
}
|
||||||
|
return { payerNames: partyANames, payeeNames: partyBNames };
|
||||||
|
},
|
||||||
|
syncPartyOptionsFromContract(contract) {
|
||||||
|
const { payerNames, payeeNames } = this.resolveContractPartyNames(contract || {});
|
||||||
|
this.payerOptions = this.buildPartySelectOptions(payerNames);
|
||||||
|
this.payeeOptions = this.buildPartySelectOptions(payeeNames);
|
||||||
|
},
|
||||||
|
isJoinedPartyName(value) {
|
||||||
|
return /[、,,]/.test(String(value || ''));
|
||||||
|
},
|
||||||
|
pickPartyValue(names, currentValue) {
|
||||||
|
const options = Array.isArray(names) ? names : [];
|
||||||
|
const current = String(currentValue || '').trim();
|
||||||
|
if (!options.length) {
|
||||||
|
// 无线上选项时(如公开查看拉不到合同),保留已有单值,丢弃多方拼接脏值
|
||||||
|
return this.isJoinedPartyName(current) ? '' : current;
|
||||||
|
}
|
||||||
|
if (current && !this.isJoinedPartyName(current) && options.includes(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return options[0] || '';
|
||||||
|
},
|
||||||
|
applyPartySelection(payerName, payeeName) {
|
||||||
|
const payerNames = this.payerOptions.map(item => item.dictKey);
|
||||||
|
const payeeNames = this.payeeOptions.map(item => item.dictKey);
|
||||||
|
this.form.payerName = this.pickPartyValue(payerNames, payerName);
|
||||||
|
this.form.payeeName = this.pickPartyValue(payeeNames, payeeName);
|
||||||
|
// 编辑历史单值不在合同多方中时,仍保留展示,避免下拉空白
|
||||||
|
if (
|
||||||
|
this.form.payerName &&
|
||||||
|
!this.payerOptions.some(item => item.dictKey === this.form.payerName)
|
||||||
|
) {
|
||||||
|
this.payerOptions = [
|
||||||
|
...this.payerOptions,
|
||||||
|
{ dictKey: this.form.payerName, dictValue: this.form.payerName },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.form.payeeName &&
|
||||||
|
!this.payeeOptions.some(item => item.dictKey === this.form.payeeName)
|
||||||
|
) {
|
||||||
|
this.payeeOptions = [
|
||||||
|
...this.payeeOptions,
|
||||||
|
{ dictKey: this.form.payeeName, dictValue: this.form.payeeName },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetchContractPartyDetail(contractId) {
|
||||||
|
if (!contractId) return null;
|
||||||
|
const { data } = await getContractDetail(contractId);
|
||||||
|
return data?.data || null;
|
||||||
|
},
|
||||||
|
async ensureContractPartyOptions(contractId, fallback = {}) {
|
||||||
|
if (!contractId) {
|
||||||
|
this.payerOptions = [];
|
||||||
|
this.payeeOptions = [];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let contract = this.contractOptions.find(item => String(item.id) === String(contractId));
|
||||||
|
const needDetail =
|
||||||
|
!contract ||
|
||||||
|
!(contract.partyAJson || contract.partyBJson) ||
|
||||||
|
this.isJoinedPartyName(contract.partyA) ||
|
||||||
|
this.isJoinedPartyName(contract.partyB) ||
|
||||||
|
(!contract.partyA && !contract.partyB);
|
||||||
|
if (needDetail) {
|
||||||
|
try {
|
||||||
|
const detail = await this.fetchContractPartyDetail(contractId);
|
||||||
|
if (detail) {
|
||||||
|
contract = {
|
||||||
|
...(contract || {}),
|
||||||
|
id: contractId,
|
||||||
|
contractNo: detail.contractNo || fallback.contractNo || contract?.contractNo,
|
||||||
|
contractName: detail.contractName || fallback.contractName || contract?.contractName,
|
||||||
|
projectId: detail.projectId || fallback.projectId || contract?.projectId,
|
||||||
|
projectName: detail.projectName || fallback.projectName || contract?.projectName,
|
||||||
|
deptId:
|
||||||
|
detail.organizationId ||
|
||||||
|
detail.deptId ||
|
||||||
|
fallback.deptId ||
|
||||||
|
contract?.deptId,
|
||||||
|
deptName:
|
||||||
|
detail.organizationName ||
|
||||||
|
detail.deptName ||
|
||||||
|
fallback.deptName ||
|
||||||
|
contract?.deptName,
|
||||||
|
partyA: detail.partyA || contract?.partyA || '',
|
||||||
|
partyB: detail.partyB || contract?.partyB || '',
|
||||||
|
partyAJson: detail.partyAJson || contract?.partyAJson || '',
|
||||||
|
partyBJson: detail.partyBJson || contract?.partyBJson || '',
|
||||||
|
settlementType:
|
||||||
|
fallback.settlementType ||
|
||||||
|
contract?.settlementType ||
|
||||||
|
(detail.contractCategory === '客户合同' ? 'receivable' : 'payable'),
|
||||||
|
settlementCurrency: detail.settlementCurrency || contract?.settlementCurrency,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 合同详情失败时仍用现有快照拆分多方名称
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!contract) {
|
||||||
|
contract = {
|
||||||
|
id: contractId,
|
||||||
|
...fallback,
|
||||||
|
partyA: fallback.partyA || '',
|
||||||
|
partyB: fallback.partyB || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const index = this.contractOptions.findIndex(item => String(item.id) === String(contractId));
|
||||||
|
if (index >= 0) this.contractOptions.splice(index, 1, contract);
|
||||||
|
else this.contractOptions.push(contract);
|
||||||
|
this.syncPartyOptionsFromContract(contract);
|
||||||
|
return contract;
|
||||||
|
},
|
||||||
async saveDraft(shouldSubmit) {
|
async saveDraft(shouldSubmit) {
|
||||||
await this.$refs.formRef?.validate();
|
await this.$refs.formRef?.validate();
|
||||||
if (shouldSubmit && !this.details.length) {
|
if (shouldSubmit && !this.details.length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user