diff --git a/web-admin/src/utils/mp-crm-quote-card.test.ts b/web-admin/src/utils/mp-crm-quote-card.test.ts new file mode 100644 index 000000000..45bdf1c04 --- /dev/null +++ b/web-admin/src/utils/mp-crm-quote-card.test.ts @@ -0,0 +1,132 @@ +// web-admin/src/utils/mp-crm-quote-card.test.ts +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { + MP_QUOTE_MANAGE_TABS, + MP_QUOTE_APPROVE_TABS, + buildQuoteListQuery, + buildQuoteCard, + formatQuoteMoney, + quoteTypeLabel, + quoteTypeTagType +} from './mp-crm-quote-card' + +describe('tabs', () => { + it('manage has all/submitted/accepted; approve has submitted/accepted', () => { + assert.deepEqual( + MP_QUOTE_MANAGE_TABS.map((x) => x.value), + ['all', 'submitted', 'accepted'] + ) + assert.deepEqual( + MP_QUOTE_APPROVE_TABS.map((x) => x.value), + ['submitted', 'accepted'] + ) + }) +}) + +describe('buildQuoteListQuery', () => { + it('manage all excludes invalid and trims keyword', () => { + assert.deepEqual( + buildQuoteListQuery({ + mode: 'manage', + view: 'all', + pageNum: 1, + pageSize: 20, + keyword: ' 酸 ', + currentUserId: 'u1' + }), + { + pageNum: 1, + pageSize: 20, + keyword: '酸', + excludeStatus: 'invalid' + } + ) + }) + + it('manage submitted sets status only', () => { + const q = buildQuoteListQuery({ + mode: 'manage', + view: 'submitted', + pageNum: 2, + pageSize: 10, + keyword: '', + currentUserId: 'u1' + }) + assert.equal(q.status, 'submitted') + assert.equal(q.excludeStatus, undefined) + assert.equal('pendingApprovalUserId' in q, false) + assert.equal('keyword' in q, false) + }) + + it('manage accepted sets status accepted', () => { + const q = buildQuoteListQuery({ + mode: 'manage', + view: 'accepted', + pageNum: 1, + pageSize: 20, + keyword: '', + currentUserId: 'u1' + }) + assert.equal(q.status, 'accepted') + assert.equal('approvedByUserId' in q, false) + }) + + it('approve submitted uses pendingApprovalUserId', () => { + const q = buildQuoteListQuery({ + mode: 'approve', + view: 'submitted', + pageNum: 1, + pageSize: 20, + keyword: '', + currentUserId: 'u9' + }) + assert.equal(q.status, 'submitted') + assert.equal(q.pendingApprovalUserId, 'u9') + }) + + it('approve accepted uses approvedByUserId', () => { + const q = buildQuoteListQuery({ + mode: 'approve', + view: 'accepted', + pageNum: 1, + pageSize: 20, + keyword: 'x', + currentUserId: 'u9' + }) + assert.equal(q.approvedByUserId, 'u9') + assert.equal(q.keyword, 'x') + assert.equal('status' in q && q.status !== undefined, false) + }) +}) + +describe('buildQuoteCard', () => { + it('maps fields and money', () => { + const card = buildQuoteCard({ + id: 'q1', + quoteNo: 'BJ001', + quoteTitle: '硫酸报价', + customerName: '甲公司', + quoteType: 'sales', + finalAmount: 1234.5, + status: 'draft', + quoteDate: '2026-10-01' + }) + assert.equal(card.id, 'q1') + assert.equal(card.quoteNo, 'BJ001') + assert.equal(card.quoteTypeLabel, '销售') + assert.equal(card.quoteTypeTagType, 'success') + assert.equal(card.amountText, '¥1,234.50') + assert.equal(card.status, 'draft') + }) +}) + +describe('quoteType helpers', () => { + it('maps purchase/sales', () => { + assert.equal(quoteTypeLabel('purchase'), '采购') + assert.equal(quoteTypeTagType('purchase'), 'warning') + assert.equal(quoteTypeLabel('sales'), '销售') + assert.equal(formatQuoteMoney(1000), '¥1,000.00') + assert.equal(formatQuoteMoney(undefined), '—') + }) +}) diff --git a/web-admin/src/utils/mp-crm-quote-card.ts b/web-admin/src/utils/mp-crm-quote-card.ts new file mode 100644 index 000000000..b988d693e --- /dev/null +++ b/web-admin/src/utils/mp-crm-quote-card.ts @@ -0,0 +1,96 @@ +export type MpQuoteMode = 'manage' | 'approve' +export type MpQuoteView = 'all' | 'submitted' | 'accepted' + +export const MP_QUOTE_MANAGE_TABS: Array<{ value: MpQuoteView; label: string }> = [ + { value: 'all', label: '全部' }, + { value: 'submitted', label: '待审批' }, + { value: 'accepted', label: '已审批' } +] + +export const MP_QUOTE_APPROVE_TABS: Array<{ value: Exclude; label: string }> = [ + { value: 'submitted', label: '待审批' }, + { value: 'accepted', label: '已审批' } +] + +export function quoteTypeLabel(type?: string): string { + if ((type || '').trim() === 'purchase') return '采购' + return '销售' +} + +export function quoteTypeTagType(type?: string): 'success' | 'warning' { + if ((type || '').trim() === 'purchase') return 'warning' + return 'success' +} + +export function formatQuoteMoney(amount?: number | null): string { + if (amount === undefined || amount === null || Number.isNaN(Number(amount))) return '—' + return `¥${Number(amount).toLocaleString('zh-CN', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + })}` +} + +export function buildQuoteListQuery(input: { + mode: MpQuoteMode + view: MpQuoteView + pageNum: number + pageSize: number + keyword: string + currentUserId: string +}): Record { + const params: Record = { + pageNum: input.pageNum, + pageSize: input.pageSize + } + const kw = String(input.keyword || '').trim() + if (kw) params.keyword = kw + + if (input.mode === 'manage') { + if (input.view === 'all') { + params.excludeStatus = 'invalid' + } else if (input.view === 'submitted') { + params.status = 'submitted' + } else { + params.status = 'accepted' + } + return params + } + + // approve + if (input.view === 'submitted') { + params.status = 'submitted' + params.pendingApprovalUserId = input.currentUserId + } else { + params.approvedByUserId = input.currentUserId + } + return params +} + +export function buildQuoteCard(row: { + id: string | number + quoteNo?: string + quoteTitle?: string + customerName?: string + quoteType?: string + finalAmount?: number + totalAmount?: number + status?: string + quoteDate?: string +}) { + const amount = + row.finalAmount !== undefined && row.finalAmount !== null + ? row.finalAmount + : row.totalAmount + return { + id: String(row.id), + quoteNo: row.quoteNo || '—', + quoteTitle: row.quoteTitle || '—', + customerName: row.customerName || '—', + quoteType: row.quoteType || 'sales', + quoteTypeLabel: quoteTypeLabel(row.quoteType), + quoteTypeTagType: quoteTypeTagType(row.quoteType), + amountText: formatQuoteMoney(amount), + status: row.status || '', + quoteDate: row.quoteDate || '—' + } +}