1、调整导入运单

2、调整对账
This commit is contained in:
2026-09-09 14:43:38 +08:00
parent 6d4961f404
commit f78e61b433
7 changed files with 639 additions and 69 deletions
@@ -523,23 +523,62 @@
<el-button type="primary" plain @click="openFeeAdjustment('补款')">补款</el-button>
<el-button type="primary" plain @click="openFeeAdjustment('扣款')">扣款</el-button>
</div>
<el-table :data="completionSummaryRows" border>
<el-table
:data="completionSummaryRows"
border
show-summary
:summary-method="getCompletionSummarySums"
>
<el-table-column type="index" label="序号" width="72" align="center" />
<el-table-column prop="name" label="费用项目" min-width="150" align="center" />
<el-table-column prop="name" label="费用项目" min-width="150" align="center">
<template #default="{ row }">
<span :class="{ 'completion-dialog__negative': row._feeKind === '扣款' }">{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column label="原金额(元)" min-width="160" align="right">
<template #default="{ row }">{{ formatCompletionMoney(row.originalAmount) }}</template>
</el-table-column>
<el-table-column label="调整金额(元)" min-width="160" align="right">
<template #default="{ row }"
><span :class="{ 'completion-dialog__negative': row.adjustAmount < 0 }">{{
<template #default="{ row }">
<el-input-number
v-if="editable && row.manualFlag === 1"
v-model="row.adjustAmount"
:class="{ 'completion-dialog__negative-input': row._feeKind === '扣款' }"
:precision="2"
:step="0.01"
:controls="false"
:value-on-clear="0"
@change="recalculateCompletionFee(row)"
/>
<span v-else :class="{ 'completion-dialog__negative': Number(row.adjustAmount) < 0 }">{{
formatCompletionMoney(row.adjustAmount)
}}</span></template
>
}}</span>
</template>
</el-table-column>
<el-table-column label="调整后金额(元)" min-width="180" align="right">
<template #default="{ row }">{{ formatCompletionMoney(row.afterAmount) }}</template>
<template #default="{ row }">
<span :class="{ 'completion-dialog__negative': Number(row.afterAmount) < 0 }">{{
formatCompletionMoney(row.afterAmount)
}}</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="220">
<template #default="{ row }">
<el-input
v-if="editable"
v-model="row.remark"
maxlength="200"
show-word-limit
placeholder="请输入备注"
/>
<span v-else>{{ displayValue(row.remark) }}</span>
</template>
</el-table-column>
<el-table-column v-if="editable" label="操作" width="90" align="center">
<template #default="{ row }">
<el-link v-if="row.manualFlag === 1" type="danger" @click="removeCompletionFee(row)">删除</el-link>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="220" />
</el-table>
</section>
</div>
@@ -565,6 +604,7 @@ import * as api from '@/api/settlement/transportReconciliation';
import * as formalSettlementApi from '@/api/settlement/formalSettlement';
import { getDictionary } from '@/api/system/dictbiz';
import { Search } from '@element-plus/icons-vue';
import { h } from 'vue';
import { downloadXls } from '@/utils/util';
import { transportReconciliationFormFields } from '@/option/settlement/transportReconciliationForm';
import {
@@ -610,6 +650,7 @@ export default {
adjustDialog: { visible: false, saving: false, rows: [], reason: '' },
manualMatchDialog: { visible: false, loading: false, row: null },
completionDialog: { visible: false, loading: false },
completionSummaryRowsData: null,
externalEditing: {},
externalEditSnapshots: {},
matchingStarted: false,
@@ -728,9 +769,17 @@ export default {
return [...names];
},
duplicateRows() {
return this.externalDetails.filter(
row => row.suspectedDuplicate || row.matchStatus === 'suspected_duplicate'
const duplicateKeys = new Set();
const duplicateRows = [];
this.externalDetails.forEach(row => {
const key = this.externalDuplicateKey(row);
if (duplicateKeys.has(key)) duplicateRows.push(row);
else duplicateKeys.add(key);
});
const duplicateKeySet = new Set(
duplicateRows.map(row => this.externalDuplicateKey(row))
);
return this.externalDetails.filter(row => duplicateKeySet.has(this.externalDuplicateKey(row)));
},
canStartMatch() {
return Boolean(this.form.formalSettlementId && this.externalDetails.length);
@@ -767,6 +816,11 @@ export default {
return this.form.reconciliationModeName || labels[this.form.reconciliationMode] || '-';
},
completionSummaryRows() {
if (Array.isArray(this.completionSummaryRowsData)) return this.completionSummaryRowsData;
return this.buildCompletionSummaryRows;
},
buildCompletionSummaryRows() {
const source =
this.form.feeSummary ||
this.form.feeSummaries ||
@@ -796,10 +850,14 @@ export default {
: originalAmount;
return {
name,
feeType: '',
feeItem: name,
originalAmount,
adjustAmount: afterAmount - originalAmount,
afterAmount,
remark: '',
manualFlag: 0,
_feeKind: '',
_index: index,
};
});
@@ -851,6 +909,7 @@ export default {
this.externalDetails = [];
this.externalEditing = {};
this.externalEditSnapshots = {};
this.completionSummaryRowsData = null;
this.matchingStarted = false;
this.externalTab = 'all';
this.internalQuery = { documentNo: '', vehicleNo: '', batchNo: '', cargoName: '' };
@@ -880,6 +939,7 @@ export default {
: data.payeeName) ||
'',
};
this.completionSummaryRowsData = null;
const internalRows = data.internalDetails || data.internalBillDetails || data.internals || [];
this.internalDetails = internalRows.map(row => this.normalizeInternalRow(row));
const externalRows = data.externalDetails || data.externalBillDetails || data.externals || [];
@@ -908,6 +968,52 @@ export default {
return {};
}
},
externalDuplicateKey(row) {
const feeItems = row.feeItems || this.parseFeeItems(row.feeItemsJson);
const normalized = value => {
if (value === null || value === undefined || value === '') return '';
if (typeof value === 'number') return Number(value).toString();
if (typeof value === 'object') {
return JSON.stringify(
Object.keys(value)
.sort()
.reduce((result, key) => {
result[key] = normalized(value[key]);
return result;
}, {})
);
}
const text = String(value).trim().replace(/\s+/g, '').toLowerCase();
return /^-?\d+(\.\d+)?$/.test(text) ? Number(text).toString() : text;
};
const normalizeDate = value => {
if (!value) return '';
const date = this.$dayjs(value);
return date.isValid() ? date.format('YYYY-MM-DDTHH:mm:ss') : value;
};
return [
row.vehicleNo,
row.departureAddress,
row.arrivalAddress,
normalizeDate(row.actualDepartureTime),
normalizeDate(row.actualCompletionTime),
row.transportType,
row.cargoName,
row.cargoType,
row.specification,
row.model,
row.transportQuantity,
row.quantityUnit,
row.mileage,
row.batchNo,
row.unitPrice,
row.freightAmount,
feeItems,
row.settlementAmount,
]
.map(normalized)
.join('|');
},
normalizeInternalRow(row) {
const feeItems =
row.feeItems && typeof row.feeItems === 'object' && Object.keys(row.feeItems).length
@@ -1027,6 +1133,7 @@ export default {
if (formalSettlementChanged) {
this.externalDetails = [];
this.matchingStarted = false;
this.completionSummaryRowsData = null;
}
await this.loadFormalInternalPreview(selected.id);
this.formalDialog.visible = false;
@@ -1174,6 +1281,7 @@ export default {
this.externalDetails = (data.externalDetails || []).map((row, index) =>
this.normalizeExternalRow(row, index)
);
this.completionSummaryRowsData = null;
this.matchingStarted = true;
this.$message.success('匹配完成');
} finally {
@@ -1206,10 +1314,22 @@ export default {
) {
return this.$message.warning('差异单数、差异货量和差异金额必须全部为0才可完成对账');
}
this.completionSummaryRowsData = this.buildCompletionSummaryRows.map(row => ({ ...row }));
this.completionDialog.visible = true;
},
async confirmCompletion() {
if (this.completionDialog.loading) return;
for (const row of this.completionSummaryRows) {
const adjustAmount = Number(row.adjustAmount || 0);
if (row.manualFlag === 1 && row._feeKind === '补款' && adjustAmount <= 0) {
return this.$message.warning('补款调整金额必须大于0');
}
if (row.manualFlag === 1 && row._feeKind === '扣款' && adjustAmount >= 0) {
return this.$message.warning('扣款调整金额必须小于0');
}
if (String(row.remark || '').length > 200) return this.$message.warning('备注不能超过200个字');
this.recalculateCompletionFee(row);
}
this.completionDialog.loading = true;
try {
this.actionLoading = true;
@@ -1220,6 +1340,16 @@ export default {
remark: this.form.remark,
internalDetails: this.serializeInternalRows(),
externalDetails: this.serializeExternalRows(),
feeSummary: this.completionSummaryRows.map(row => ({
id: row.id || undefined,
feeType: row.feeType,
feeItem: row.feeItem || row.name,
originalAmount: Number(row.originalAmount || 0),
adjustAmount: Number(row.adjustAmount || 0),
settlementAmount: Number(row.afterAmount || 0),
remark: row.remark || '',
manualFlag: Number(row.manualFlag || 0),
})),
})
);
this.form = { ...this.form, ...data };
@@ -1237,7 +1367,55 @@ export default {
}
},
openFeeAdjustment(type) {
this.$message.info(`${type}请在结算明细调整中维护`);
if (!Array.isArray(this.completionSummaryRowsData)) {
this.completionSummaryRowsData = this.buildCompletionSummaryRows.map(row => ({ ...row }));
}
this.completionSummaryRowsData.push({
id: null,
name: type,
feeType: '其他费用',
feeItem: type,
originalAmount: 0,
adjustAmount: 0,
afterAmount: 0,
remark: '',
manualFlag: 1,
_feeKind: type,
_index: this.completionSummaryRowsData.length,
});
},
removeCompletionFee(row) {
if (row.manualFlag !== 1 || !Array.isArray(this.completionSummaryRowsData)) return;
const index = this.completionSummaryRowsData.indexOf(row);
if (index >= 0) this.completionSummaryRowsData.splice(index, 1);
},
recalculateCompletionFee(row) {
row.originalAmount = row.manualFlag === 1 ? 0 : Number(row.originalAmount || 0);
row.afterAmount = Number(row.originalAmount || 0) + Number(row.adjustAmount || 0);
},
getCompletionSummarySums({ columns, data }) {
return columns.map((column, index) => {
if (index === 0) return '合计';
const amountPropertyByIndex = {
2: 'originalAmount',
3: 'adjustAmount',
4: 'afterAmount',
};
const property = column.property || amountPropertyByIndex[index];
if (property === 'originalAmount' || property === 'adjustAmount' || property === 'afterAmount') {
const total = data.reduce((sum, row) => {
const value = property === 'afterAmount' ? row.afterAmount : row[property];
return sum + Number(value || 0);
}, 0);
const text = this.formatCompletionMoney(total);
const negativeAmount =
(property === 'adjustAmount' || property === 'afterAmount') && total < 0;
return negativeAmount
? h('span', { class: 'completion-dialog__negative', style: { color: '#f56c6c' } }, text)
: text;
}
return '';
});
},
sumFeeAmount(rows, name) {
return rows.reduce((sum, row) => {
@@ -1254,11 +1432,16 @@ export default {
item.afterAmount ?? item.adjustedAmount ?? item.settlementAmount ?? item.totalAmount ?? originalAmount
);
return {
id: item.id,
feeType: item.feeType || '',
feeItem: item.feeItem || item.name || item.feeItemName || '',
name: item.name || item.feeItem || item.feeItemName || `费用${index + 1}`,
originalAmount,
adjustAmount: Number(item.adjustAmount ?? item.adjustmentAmount ?? afterAmount - originalAmount),
afterAmount,
remark: item.remark || item.adjustRemark || '',
manualFlag: Number(item.manualFlag || 0),
_feeKind: item.feeItem === '补款' || item.feeItem === '扣款' ? item.feeItem : '',
_index: index,
};
},
@@ -1781,6 +1964,9 @@ export default {
.completion-dialog__negative {
color: #f56c6c;
}
.completion-dialog__negative-input :deep(input) {
color: #f56c6c;
}
.completion-dialog__footer {
display: flex;
justify-content: center;