Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eae5654f01 | ||
|
|
71927bedef |
No files matched your search
@@ -512,6 +512,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { fetchCustomerOptions } from '@/api/crm/customer'
|
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 AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
||||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||||
import MpBusinessHeader from '@/components/mp/MpBusinessHeader.vue'
|
import MpBusinessHeader from '@/components/mp/MpBusinessHeader.vue'
|
||||||
@@ -540,11 +542,14 @@
|
|||||||
} from '@/views/pl/quota-cert/model'
|
} from '@/views/pl/quota-cert/model'
|
||||||
import {
|
import {
|
||||||
bindingsFromPurchaseCerts,
|
bindingsFromPurchaseCerts,
|
||||||
contractsForCustomer,
|
|
||||||
exactCustomerMatches,
|
exactCustomerMatches,
|
||||||
matchPurchaseCert,
|
matchPurchaseCert,
|
||||||
scopeProductsFromLines
|
scopeProductsFromLines
|
||||||
} from '@/views/pl/quota-cert/contract-binder-rules'
|
} 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 { getProductUnitLabel } from '@/utils/crm-product-display'
|
||||||
import { useDictStore } from '@/store/modules/dict'
|
import { useDictStore } from '@/store/modules/dict'
|
||||||
import { showCrmConfirmDialog } from '@/utils/mp-crm-confirm'
|
import { showCrmConfirmDialog } from '@/utils/mp-crm-confirm'
|
||||||
@@ -928,19 +933,39 @@
|
|||||||
if (current === epoch) ElMessage.warning('客户匹配失败,请手工选择')
|
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 = '') {
|
async function searchContracts(keyword = '') {
|
||||||
const current = epoch
|
const current = epoch
|
||||||
|
const query = certArchiveContractListQuery(bindingCustomer.value.id, keyword)
|
||||||
|
if (!query) {
|
||||||
|
contractOptions.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const rows = await quotaCertApi.contracts(keyword)
|
const result = await listContract(query)
|
||||||
if (current !== epoch) return
|
if (current !== epoch) return
|
||||||
const taken = new Set(
|
const taken = (form.value.contractBindings || []).map(
|
||||||
(form.value.contractBindings || []).map((row: CertificateContractBinding) => row.contractId)
|
(row: CertificateContractBinding) => row.contractId
|
||||||
)
|
)
|
||||||
contractOptions.value = contractsForCustomer(rows, bindingCustomer.value).filter(
|
contractOptions.value = filterAvailableBindingContracts(result.rows || [], taken).map(
|
||||||
(row) => !taken.has(row.id)
|
toContractOption
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
if (current === epoch) contractOptions.value = []
|
if (current === epoch) {
|
||||||
|
contractOptions.value = []
|
||||||
|
ElMessage.warning('合同加载失败,请重试')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function openContracts() {
|
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