Merge remote-tracking branch 'origin/release/test' into dev
# Conflicts: # src/modules/container-loading/pages/ContainersView.vue
This commit is contained in:
@@ -465,3 +465,19 @@ export async function createFinanceCost(payload: Record<string, unknown>, vouche
|
||||
const response = await http.post<ApiEnvelope<FinanceCostResource>, ApiEnvelope<FinanceCostResource>>(`${basePath}/cost-entries`, { ...payload, currency: 'CNY', voucherFileIds }, { headers: { 'Idempotency-Key': idempotencyKey('cost') } })
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export interface FinancePaymentVoucher {
|
||||
fileId: string
|
||||
fileName: string
|
||||
contentType: string
|
||||
sizeBytes: number
|
||||
}
|
||||
export async function getFinancePaymentVoucher(customerId: string, fileId: string): Promise<FinancePaymentVoucher> {
|
||||
return unwrapApiResponse(await http.get<ApiEnvelope<FinancePaymentVoucher>, ApiEnvelope<FinancePaymentVoucher>>(
|
||||
`${basePath}/vouchers/${encodeURIComponent(fileId)}`, { params: { customerId } }))
|
||||
}
|
||||
export function downloadFinancePaymentVoucher(customerId: string, fileId: string): Promise<Blob> {
|
||||
return http.get<Blob, Blob>(`${basePath}/vouchers/${encodeURIComponent(fileId)}/content`, {
|
||||
params: { customerId }, responseType: 'blob',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ export { financeRoutes } from './routes'
|
||||
export { default as CustomerStatementsPanel } from './components/CustomerStatementsPanel.vue'
|
||||
export {
|
||||
getFinanceCustomerAccountSummary,
|
||||
getFinancePaymentVoucher,
|
||||
downloadFinancePaymentVoucher,
|
||||
getFinanceSettlement,
|
||||
listFinancePayableSettlements,
|
||||
listFinanceReceivableSettlements,
|
||||
|
||||
@@ -232,6 +232,37 @@ describe('FinanceCustomerStatementsView', () => {
|
||||
expect(vm.filteredRows).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('does not issue one detail request per statement for an ordinary query', async () => {
|
||||
vi.mocked(listFinanceStatements).mockResolvedValue(Array.from({ length: 60 }, (_, index) => ({
|
||||
...statement,
|
||||
id: String(501 + index),
|
||||
billNo: `REC-${501 + index}`,
|
||||
})))
|
||||
const { wrapper } = await mountPage()
|
||||
vi.mocked(listFinanceStatements).mockClear()
|
||||
vi.mocked(getReconciliationStatement).mockClear()
|
||||
|
||||
await (wrapper.vm as unknown as { load(model: Record<string, unknown>): Promise<void> }).load({ status: 'ISSUED' })
|
||||
await flushPromises()
|
||||
|
||||
expect(listFinanceStatements).toHaveBeenCalledTimes(1)
|
||||
expect(getReconciliationStatement).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('uses only the statement list request for an ordinary search', async () => {
|
||||
const { wrapper } = await mountPage()
|
||||
vi.mocked(listFinanceStatements).mockClear()
|
||||
vi.mocked(getReconciliationStatement).mockClear()
|
||||
|
||||
await (wrapper.vm as unknown as { load(model: Record<string, unknown>): Promise<void> }).load({
|
||||
status: 'ISSUED', billNo: '501', customerId: '', containerNo: '',
|
||||
periodStartRange: [], periodEndRange: [], settled: '', lastPaymentRange: [], createdRange: [],
|
||||
})
|
||||
|
||||
expect(listFinanceStatements).toHaveBeenCalledTimes(1)
|
||||
expect(getReconciliationStatement).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps statement status filter options aligned with the list status column', async () => {
|
||||
const { wrapper } = await mountPage()
|
||||
const vm = wrapper.vm as unknown as {
|
||||
@@ -355,10 +386,10 @@ describe('FinanceCustomerStatementsView', () => {
|
||||
expect(document.body.textContent).not.toContain('hash-81')
|
||||
})
|
||||
|
||||
it('surfaces detail hydration failures instead of silently returning an empty filtered list', async () => {
|
||||
it('surfaces container-filter detail hydration failures instead of silently returning an empty filtered list', async () => {
|
||||
const { wrapper } = await mountPage()
|
||||
vi.mocked(getReconciliationStatement).mockRejectedValueOnce(new Error('detail unavailable'))
|
||||
await (wrapper.vm as unknown as { load(model: Record<string, unknown>): Promise<void> }).load({ status: 'ISSUED' })
|
||||
await (wrapper.vm as unknown as { load(model: Record<string, unknown>): Promise<void> }).load({ status: 'ISSUED', containerNo: 'CONT-31' })
|
||||
await flushPromises()
|
||||
expect(document.body.textContent).toContain('筛选字段加载失败')
|
||||
})
|
||||
|
||||
@@ -246,7 +246,10 @@ async function loadCustomers() {
|
||||
}
|
||||
|
||||
async function load(model?: QueryModel) {
|
||||
const hydrateDetails = model !== undefined
|
||||
// The list response already contains every normal filter field. Only the
|
||||
// container-number filter still depends on statement lines from the detail API.
|
||||
// Avoid loading every statement detail for an ordinary search (N+1 requests).
|
||||
const hydrateDetails = Boolean(String(model?.containerNo || '').trim())
|
||||
if (model) {
|
||||
filters.value = { ...model }
|
||||
appliedFilters.value = { ...model }
|
||||
@@ -647,7 +650,7 @@ watch(detailRouteId, (statementId) => {
|
||||
</el-tabs>
|
||||
</DetailPageLayout>
|
||||
<ListPageLayout v-else aria-label="客户对账单">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户对账单筛选" :fields="fields" preference-key="finance-statements-query" :search-on-clear="false" @search="load" /></template>
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户对账单筛选" :fields="fields" :loading="loading" preference-key="finance-statements-query" :search-on-clear="false" @search="load" /></template>
|
||||
<template v-if="customerError || error" #state><LoadErrorState title="客户对账单加载失败" :message="(customerError || error)?.message || ''" :loading="loading" @retry="customerError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="(customerError || error)!" /></template></LoadErrorState></template>
|
||||
<template v-if="!customerError && !error">
|
||||
<section class="surface-panel table-panel">
|
||||
|
||||
Reference in New Issue
Block a user