merge: 合入证件建档按客户查询合同修复(#24)。
This commit is contained in:
commit
9f65cd447f
3 files changed
+115
-7
No files matched your search
@@ -512,6 +512,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchCustomerOptions } from '@/api/crm/customer'
|
||||
import { listContract } from '@/api/crm/contract'
|
||||
import type { ContractVO } from '@/api/crm/contract/types'
|
||||
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
import MpBusinessHeader from '@/components/mp/MpBusinessHeader.vue'
|
||||
@@ -540,11 +542,14 @@
|
||||
} from '@/views/pl/quota-cert/model'
|
||||
import {
|
||||
bindingsFromPurchaseCerts,
|
||||
contractsForCustomer,
|
||||
exactCustomerMatches,
|
||||
matchPurchaseCert,
|
||||
scopeProductsFromLines
|
||||
} from '@/views/pl/quota-cert/contract-binder-rules'
|
||||
import {
|
||||
certArchiveContractListQuery,
|
||||
filterAvailableBindingContracts
|
||||
} from '@/views/pl/quota-cert/cert-archive-contract-query'
|
||||
import { getProductUnitLabel } from '@/utils/crm-product-display'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { showCrmConfirmDialog } from '@/utils/mp-crm-confirm'
|
||||
@@ -928,19 +933,39 @@
|
||||
if (current === epoch) ElMessage.warning('客户匹配失败,请手工选择')
|
||||
}
|
||||
}
|
||||
function toContractOption(row: ContractVO): ContractOption {
|
||||
return {
|
||||
id: String(row.id),
|
||||
contractNo: row.contractNo || '',
|
||||
contractName: row.contractName || '',
|
||||
contractType: row.contractType,
|
||||
customerId: row.customerId != null ? String(row.customerId) : '',
|
||||
customerName: row.customerName,
|
||||
status: row.status,
|
||||
signDate: row.signDate || null
|
||||
}
|
||||
}
|
||||
async function searchContracts(keyword = '') {
|
||||
const current = epoch
|
||||
const query = certArchiveContractListQuery(bindingCustomer.value.id, keyword)
|
||||
if (!query) {
|
||||
contractOptions.value = []
|
||||
return
|
||||
}
|
||||
try {
|
||||
const rows = await quotaCertApi.contracts(keyword)
|
||||
const result = await listContract(query)
|
||||
if (current !== epoch) return
|
||||
const taken = new Set(
|
||||
(form.value.contractBindings || []).map((row: CertificateContractBinding) => row.contractId)
|
||||
const taken = (form.value.contractBindings || []).map(
|
||||
(row: CertificateContractBinding) => row.contractId
|
||||
)
|
||||
contractOptions.value = contractsForCustomer(rows, bindingCustomer.value).filter(
|
||||
(row) => !taken.has(row.id)
|
||||
contractOptions.value = filterAvailableBindingContracts(result.rows || [], taken).map(
|
||||
toContractOption
|
||||
)
|
||||
} catch {
|
||||
if (current === epoch) contractOptions.value = []
|
||||
if (current === epoch) {
|
||||
contractOptions.value = []
|
||||
ElMessage.warning('合同加载失败,请重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
function openContracts() {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { test } from 'node:test'
|
||||
import {
|
||||
certArchiveContractListQuery,
|
||||
filterAvailableBindingContracts
|
||||
} from './cert-archive-contract-query'
|
||||
|
||||
test('证件建档合同查询必须带客户 ID,不能只靠前端再过滤最近合同', () => {
|
||||
assert.deepEqual(certArchiveContractListQuery('cust-1', ' HT-01 '), {
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
keyword: 'HT-01',
|
||||
customerId: 'cust-1',
|
||||
excludeStatus: 'invalid',
|
||||
includeProducts: false
|
||||
})
|
||||
assert.equal(certArchiveContractListQuery(''), null)
|
||||
assert.equal(certArchiveContractListQuery(' '), null)
|
||||
assert.deepEqual(certArchiveContractListQuery('cust-2', ' ').keyword, undefined)
|
||||
})
|
||||
|
||||
test('已关联合同与不可选状态不进入添加列表', () => {
|
||||
const rows = [
|
||||
{ id: 'a', status: 'approved' },
|
||||
{ id: 'b', status: 'executing' },
|
||||
{ id: 'c', status: 'draft' },
|
||||
{ id: 'd', status: 'approved' },
|
||||
{ id: '', status: 'approved' }
|
||||
]
|
||||
assert.deepEqual(
|
||||
filterAvailableBindingContracts(rows, ['d', '']).map((item) => item.id),
|
||||
['a', 'b']
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
/** 证件建档添加合同时的 CRM 列表查询与本地过滤。 */
|
||||
|
||||
export interface CertArchiveContractListQuery {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
customerId: string
|
||||
excludeStatus: 'invalid'
|
||||
includeProducts: false
|
||||
}
|
||||
|
||||
/** 已选客户后按客户 ID 分页查询,避免只取最近合同再前端过滤。 */
|
||||
export function certArchiveContractListQuery(
|
||||
customerId: string,
|
||||
keyword = ''
|
||||
): CertArchiveContractListQuery | null {
|
||||
const id = String(customerId || '').trim()
|
||||
if (!id) return null
|
||||
const text = keyword.trim()
|
||||
return {
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
keyword: text || undefined,
|
||||
customerId: id,
|
||||
excludeStatus: 'invalid',
|
||||
includeProducts: false
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_STATUSES = ['approved', 'executing']
|
||||
|
||||
/** 去掉已关联合同和不在可选状态内的行。 */
|
||||
export function filterAvailableBindingContracts<T extends { id?: string | number; status?: string }>(
|
||||
rows: T[],
|
||||
takenIds: Iterable<string>,
|
||||
selectableStatuses: string[] = DEFAULT_STATUSES
|
||||
): T[] {
|
||||
const taken = new Set(
|
||||
[...takenIds].map((id) => String(id || '').trim()).filter(Boolean)
|
||||
)
|
||||
return rows.filter((row) => {
|
||||
const id = String(row.id || '').trim()
|
||||
if (!id || taken.has(id)) return false
|
||||
if (selectableStatuses.length && !selectableStatuses.includes(String(row.status || ''))) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user