From 441d62a874cb594e2d16437ed4ecc99ec8aee9b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com>
Date: Sat, 31 Oct 2026 16:07:04 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=B6=E7=A5=A8=E7=99=BB=E8=AE=B0?=
=?UTF-8?q?=E5=85=88=E7=A5=A8=E5=90=8E=E6=AC=BE=E8=A1=A8=E5=8D=95=E6=94=B9?=
=?UTF-8?q?=E9=80=A0=EF=BC=88=E6=89=8B=E9=80=89=E7=BB=93=E7=AE=97=E7=BB=84?=
=?UTF-8?q?=E7=BB=87/=E5=8F=91=E7=A5=A8=E6=98=8E=E7=BB=86=E5=8C=BA?=
=?UTF-8?q?=E5=9D=97/=E5=8E=BB=E5=85=B3=E8=81=94=E7=BB=93=E7=AE=97?=
=?UTF-8?q?=E5=8D=95=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/payment/invoiceReceipt.js | 6 +
src/views/payment/invoice-receipt-form.vue | 343 ++++++++-------------
2 files changed, 127 insertions(+), 222 deletions(-)
diff --git a/src/api/payment/invoiceReceipt.js b/src/api/payment/invoiceReceipt.js
index 8db2dfb..79d2fab 100644
--- a/src/api/payment/invoiceReceipt.js
+++ b/src/api/payment/invoiceReceipt.js
@@ -38,6 +38,12 @@ export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'p
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
export const pushKingdee = ids =>
request({ url: `${baseUrl}/push-kingdee`, method: 'post', timeout: 30000, data: ids });
+// 先票后款:发票明细查询(收票登记明细区块按票号带出发票明细行)
+export const invoiceDetailQuery = invoiceNo =>
+ request({ url: `${baseUrl}/invoice-detail-query`, method: 'get', params: { invoiceNo } });
+// 先票后款:结算单认领收票单候选(审批通过且未被有效应付结算单认领)
+export const claimCandidates = keyword =>
+ request({ url: `${baseUrl}/claim-candidates`, method: 'get', params: { keyword } });
export const approvalStatusOptions = [
{ label: '草稿', value: 'draft' },
diff --git a/src/views/payment/invoice-receipt-form.vue b/src/views/payment/invoice-receipt-form.vue
index b798e98..53ae453 100644
--- a/src/views/payment/invoice-receipt-form.vue
+++ b/src/views/payment/invoice-receipt-form.vue
@@ -109,34 +109,50 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ formatQuantity(row.num) }}
+
+
+ {{ formatMoney(row.unitPrice) }}
+
+
+ {{ formatMoney(row.detailAmount) }}
+
+
+ {{ formatRateText(row.taxRate) }}
+
+
+ {{ formatMoney(row.taxAmount) }}
+
+
+
+
+
-
+
{{ formatMoney(row.receivedInvoiceAmount) }}
-
-
-
-
+
+ {{ formatMoney(row.allocatedInvoiceAmount) }}
@@ -216,71 +223,6 @@
:query-api="apiQueryInvoicePool"
@confirm="handleInvoicePicked"
/>
-
-
-
-
- 查询
-
-
-
-
-
-
-
-
-
-
- {{ formatMoney(row.settlementAmount) }}
-
-
- {{ formatMoney(row.receivedInvoiceAmount) }}
-
-
-
- 选择
-
-
-
-
- 取消
-
- 确定{{ settlementDialog.selection.length ? `(${settlementDialog.selection.length})` : '' }}
-
-
-
@@ -288,6 +230,7 @@
import { Search } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/invoiceReceipt';
+import { getDeptTree } from '@/api/system/dept';
import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { getMkPublicDetail } from '@/api/mk-process';
@@ -310,6 +253,7 @@ const emptyForm = () => ({
customerEmails: [],
departmentEmails: [],
projectName: '',
+ deptId: null,
deptName: '',
payerName: '',
payeeName: '',
@@ -330,26 +274,24 @@ export default {
form: emptyForm(),
customerEmailOptions: [],
departmentEmailOptions: [],
+ invoiceItems: [],
+ deptTreeOptions: [],
+ deptMap: new Map(),
+ // 结算组织=合同承办部门:部门树级联单选(取末级 id),后端沿部门树上溯平台公司取金蝶组织
+ deptCascaderProps: { checkStrictly: true, emitPath: true },
invoiceDialog: {
visible: false,
},
- settlementDialog: {
- visible: false,
- loading: false,
- keyword: '',
- rows: [],
- selection: [],
- },
rules: {
invoiceNo: [{ required: true, validator: this.validateInvoice, trigger: 'change' }],
- settlementIds: [{ required: true, validator: this.validateSettlements, trigger: 'change' }],
+ deptId: [{ required: true, validator: this.validateDept, trigger: 'change' }],
customerEmails: [{ validator: this.validateEmails, trigger: 'change' }],
departmentEmails: [{ validator: this.validateEmails, trigger: 'change' }],
},
};
},
computed: {
- ...mapGetters(['permission']),
+ ...mapGetters(['permission', 'userInfo']),
recordId() {
return this.$route.query.id || '';
},
@@ -363,14 +305,11 @@ export default {
const code = this.recordId ? 'invoice_receipt_edit' : 'invoice_receipt_add';
return this.hasPermission(code);
},
- settlementLabel() {
- return this.form.settlements
- .map(item => item.formalSettlementNo)
- .filter(Boolean)
- .join(',');
- },
- settlementIds() {
- return this.form.settlements.map(item => item.formalSettlementId || item.settlementId);
+ deptCascaderValue() {
+ if (this.form.deptId === null || this.form.deptId === undefined || this.form.deptId === '') {
+ return [];
+ }
+ return [String(this.form.deptId)];
},
},
created() {
@@ -406,6 +345,7 @@ export default {
.filter(Boolean);
},
async initialize() {
+ this.loadDepartments();
if (this.isPublicViewPage) {
await this.loadPublicDetail();
return;
@@ -419,14 +359,13 @@ export default {
}
const data = this.unwrapData(await api.getDetail(this.recordId));
this.applyDetail(data);
- if (this.settlementIds.length) await this.loadReferenceInformation();
},
async loadPublicDetail() {
if (!this.recordId) return;
const data = this.unwrapData(await getMkPublicDetail('invoice-receipt', this.recordId));
this.applyDetail(data);
},
- applyDetail(data) {
+ async applyDetail(data) {
this.form = {
...emptyForm(),
...data,
@@ -438,6 +377,7 @@ export default {
})),
attachments: this.parse(data.attachmentsJson),
};
+ await this.loadInvoiceDetail();
},
validateInvoice(rule, value, callback) {
if (!value) {
@@ -446,9 +386,9 @@ export default {
}
callback();
},
- validateSettlements(rule, value, callback) {
- if (!this.form.settlements.length) {
- callback(new Error('请选择应付正式结算单'));
+ validateDept(rule, value, callback) {
+ if (this.form.deptId === null || this.form.deptId === undefined || this.form.deptId === '') {
+ callback(new Error('请选择结算组织'));
return;
}
callback();
@@ -469,7 +409,7 @@ export default {
openInvoiceDialog() {
this.invoiceDialog.visible = true;
},
- confirmInvoice(row, options = {}) {
+ async confirmInvoice(row, options = {}) {
const selected = row;
if (!selected) {
this.$message.warning('请选择一张金蝶票据池发票');
@@ -504,92 +444,63 @@ export default {
...new Set([...this.departmentEmailOptions, ...this.form.departmentEmails]),
];
this.invoiceDialog.visible = false;
+ await this.loadInvoiceDetail();
this.$refs.formRef?.validateField('invoiceNo').catch(() => {});
return true;
},
- async openSettlementDialog() {
- this.settlementDialog.visible = true;
- this.settlementDialog.selection = [];
- await this.loadSettlementCandidates();
- await this.$nextTick();
- const selectedIds = new Set(this.settlementIds.map(id => String(id)));
- this.settlementDialog.rows.forEach(row => {
- if (selectedIds.has(String(row.id))) {
- this.$refs.settlementTable?.toggleRowSelection(row, true);
- }
- });
- },
- async loadSettlementCandidates() {
- this.settlementDialog.loading = true;
+ // 发票明细区块:按票号带出金蝶发票明细行(A801-1 镜像,推送 A303-2 分录同源)
+ async loadInvoiceDetail() {
+ if (!this.form.invoiceNo) {
+ this.invoiceItems = [];
+ return;
+ }
try {
- this.settlementDialog.rows =
- this.unwrapData(
- await api.getSettlementCandidates(this.settlementDialog.keyword, this.form.id)
- ) || [];
- } finally {
- this.settlementDialog.loading = false;
+ this.invoiceItems = this.unwrapData(await api.invoiceDetailQuery(this.form.invoiceNo)) || [];
+ } catch {
+ this.invoiceItems = [];
}
},
- async confirmSettlements() {
- const rows = this.settlementDialog.selection;
- if (!rows.length) {
- this.$message.warning('请至少选择一张应付正式结算单');
- return;
- }
- const first = rows[0];
- const incompatible = rows.some(
- item =>
- String(item.projectId) !== String(first.projectId) ||
- String(item.deptId) !== String(first.deptId) ||
- item.payerName !== first.payerName ||
- item.payeeName !== first.payeeName
- );
- if (incompatible) {
- this.$message.warning('关联结算单必须属于同一项目、组织及收付款方');
- return;
- }
- const previousAmount = new Map(
- this.form.settlements.map(item => [
- String(item.formalSettlementId || item.settlementId),
- item.allocatedInvoiceAmount,
- ])
- );
- this.form.settlements = rows.map(item => ({
- ...item,
- formalSettlementId: item.id,
- settlementId: item.id,
- allocatedInvoiceAmount: previousAmount.has(String(item.id))
- ? Number(previousAmount.get(String(item.id)) || 0)
- : rows.length === 1
- ? Number(item.settlementAmount || 0)
- : 0,
+ loadDepartments() {
+ // 结算组织取租户部门树(过滤外部组织),选中取末级部门 id
+ getDeptTree(this.userInfo?.tenantId)
+ .then(response => {
+ const rows = this.excludeExternalOrganization(this.unwrapData(response) || []);
+ this.deptTreeOptions = this.toDeptCascaderOptions(rows);
+ this.deptMap = new Map();
+ this.flattenDepartments(rows).forEach(item =>
+ this.deptMap.set(String(item.id), item.title || item.deptName || item.name)
+ );
+ })
+ .catch(() => {});
+ },
+ excludeExternalOrganization(list = []) {
+ return list.reduce((result, item) => {
+ const deptName = item.title || item.deptName || item.name;
+ if (String(deptName || '').trim() === '外部组织') return result;
+ result.push({
+ ...item,
+ children: item.children?.length
+ ? this.excludeExternalOrganization(item.children)
+ : item.children,
+ });
+ return result;
+ }, []);
+ },
+ toDeptCascaderOptions(list = []) {
+ return list.map(item => ({
+ label: item.title || item.deptName || item.name,
+ value: item.id == null || item.id === '' ? '' : String(item.id),
+ children: item.children?.length ? this.toDeptCascaderOptions(item.children) : undefined,
}));
- this.form.projectName = first.projectName;
- this.form.deptName = first.deptName;
- this.form.payerName = first.payerName;
- this.form.payeeName = first.payeeName;
- this.settlementDialog.visible = false;
- await this.loadReferenceInformation();
- this.$refs.formRef?.validateField('settlementIds').catch(() => {});
},
- selectSettlement(row) {
- this.$refs.settlementTable?.toggleRowSelection(row);
+ flattenDepartments(rows) {
+ return (rows || []).flatMap(item => [item, ...this.flattenDepartments(item.children || [])]);
},
- async loadReferenceInformation() {
- const data = this.unwrapData(await api.getReferenceInformation(this.settlementIds.join(',')));
- this.customerEmailOptions = data.customerEmails || [];
- this.departmentEmailOptions = [
- ...new Set([...this.departmentEmailOptions, ...(data.departmentEmails || [])]),
- ];
- if (!this.form.customerEmails.length && this.customerEmailOptions.length) {
- this.form.customerEmails = [this.customerEmailOptions[0]];
- }
- },
- remainingAmount(row) {
- return Math.max(
- Number(row.settlementAmount || 0) - Number(row.receivedInvoiceAmount || 0),
- 0
- );
+ handleDeptChange(value) {
+ const deptId = Array.isArray(value) ? value.at(-1) : value;
+ this.form.deptId = deptId || null;
+ this.form.deptName = deptId ? this.deptMap.get(String(deptId)) || '' : '';
+ this.$refs.formRef?.validateField('deptId').catch(() => {});
},
openSettlementDetail(row) {
const id = row.formalSettlementId || row.settlementId || row.id;
@@ -602,15 +513,6 @@ export default {
validateBusiness() {
const invoiceAmount = Number(this.form.invoiceAmount || 0);
if (invoiceAmount <= 0) throw new Error('开票金额必须大于0');
- for (const row of this.form.settlements) {
- const allocated = Number(row.allocatedInvoiceAmount);
- if (!Number.isFinite(allocated) || allocated < 0) {
- throw new Error('分摊发票金额必须大于或等于0');
- }
- if (allocated - this.remainingAmount(row) > 0.001) {
- throw new Error(`结算单${row.formalSettlementNo}的累计收票金额不能超过结算总金额`);
- }
- }
},
payload() {
return {
@@ -631,10 +533,9 @@ export default {
departmentEmails: this.form.departmentEmails.join(';'),
attachmentsJson: JSON.stringify(this.form.attachments || []),
remark: this.form.remark,
- settlements: this.form.settlements.map(item => ({
- settlementId: item.formalSettlementId || item.settlementId,
- allocatedInvoiceAmount: item.allocatedInvoiceAmount,
- })),
+ // 结算组织=页面手选部门(先票后款不挂结算单,认领关系由结算单侧维护,不再传 settlements)
+ deptId: this.form.deptId,
+ deptName: this.form.deptName,
};
},
async saveDraft() {
@@ -678,8 +579,7 @@ export default {
this.$message.warning('金蝶票池未查询到该发票');
return;
}
- if (!this.confirmInvoice(invoice, { preserveUserInput: true })) return;
- if (this.settlementIds.length) await this.loadReferenceInformation();
+ if (!(await this.confirmInvoice(invoice, { preserveUserInput: true }))) return;
this.$message.success('同步成功');
},
goBack() {
@@ -688,6 +588,14 @@ export default {
formatMoney(value) {
return Number(value || 0).toFixed(2);
},
+ formatQuantity(value) {
+ const number = Number(value);
+ return Number.isFinite(number) ? String(number) : '-';
+ },
+ formatRateText(value) {
+ if (value === null || value === undefined || value === '') return '-';
+ return `${value}%`;
+ },
formatRate(value) {
return value === null || value === undefined || value === '' ? '' : `${Number(value)}%`;
},
@@ -738,15 +646,6 @@ export default {
:global(.avue-layout--horizontal .receipt-form-page__actions) {
left: 0;
}
-.receipt-form-page__dialog-search {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- margin-bottom: 12px;
-}
-.receipt-form-page__dialog-search .el-input {
- width: 360px;
-}
.receipt-form-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
}