import {it,expect} from 'vitest' import {mkdtemp,rm} from 'node:fs/promises' import {tmpdir} from 'node:os' import path from 'node:path' import {Ledger} from '../src/main/ledger' import {defaultSettings} from '../src/shared/settings' function input(school:string|undefined,grade:string|undefined,studentName:string,item:string){return {date:'2026-10-19',school,grade,studentName,customer:[school,grade,studentName].filter(Boolean).join(' '),item,amountCents:100,cashier:'',reviewer:''}} it('按同一张收据组合筛选,支持姓名和中间编号,并返回可直接预览的匹配信息',async()=>{ const dir=await mkdtemp(path.join(tmpdir(),'receipt-search-')) try{ const ledger=new Ledger(path.join(dir,'db.sqlite'));await ledger.open() const batch=await ledger.reserve({name:'收据模板.xlsx',hash:'a'},[input('甲学校','高一','张三','服务费'),input('乙学校','高二','李四','资料费'),input('甲学校','高一','王五','服务费')],defaultSettings(dir),null) expect(ledger.summaries({school:'甲学校',grade:'高二',item:'资料费'})).toEqual([]) expect(ledger.summaries({search:'李四',school:'甲学校'})).toEqual([]) expect(ledger.summaries({search:' sj00000002 ',school:'乙学校',grade:'高二',item:'资料费'})[0]).toMatchObject({id:batch.id,count:3,match:{count:1,number:'SJ00000002',customer:'乙学校 高二 李四'}}) expect(ledger.summaries({search:'王五'})[0].match?.number).toBe('SJ00000003') expect(ledger.summaries({school:'甲学校'})[0].match?.count).toBe(2) expect(ledger.summaries({search:'收据模板.xlsx'})).toHaveLength(1) expect(ledger.summaries()[0].groups).toEqual([{school:'甲学校',grade:'高一',item:'服务费',count:2,totalCents:200},{school:'乙学校',grade:'高二',item:'资料费',count:1,totalCents:100}]) expect(ledger.nextNumber('SJ')).toBe('SJ00000004') }finally{await rm(dir,{recursive:true,force:true})} }) it('查找和下拉选项覆盖超过最近 200 批的旧记录,旧数据缺失学校年级也能查找',async()=>{ const dir=await mkdtemp(path.join(tmpdir(),'receipt-old-search-')) try{ const ledger=new Ledger(path.join(dir,'db.sqlite'));await ledger.open() const oldest=await ledger.reserve({name:'旧记录.xlsx',hash:'old'},[input(undefined,undefined,'旧姓名','旧项目')],defaultSettings(dir),null) oldest.outputDirectory=path.join(dir,'old-output');oldest.status='completed';await ledger.update(oldest) for(let i=0;i<200;i++)await ledger.reserve({name:'新记录.xlsx',hash:String(i)},[input('新学校','高一','新姓名','新项目')],defaultSettings(dir),null) expect(ledger.summaries()).toHaveLength(200) expect(ledger.summaries().some(batch=>batch.id===oldest.id)).toBe(false) expect(ledger.batchPage({page:41})).toMatchObject({total:201,page:41,pageSize:5,items:[{id:oldest.id}]}) expect(ledger.summaries({search:'旧姓名',school:'未分类学校',grade:'未分类年级',item:'旧项目'})[0].id).toBe(oldest.id) expect(ledger.isOutputDirectory(oldest.outputDirectory)).toBe(true) expect(ledger.isOutputDirectory(path.join(dir,'other'))).toBe(false) expect(ledger.historyFilters()).toMatchObject({schools:expect.arrayContaining(['新学校','未分类学校']),grades:expect.arrayContaining(['高一','未分类年级']),items:expect.arrayContaining(['旧项目','新项目'])}) expect(ledger.nextNumber('SJ')).toBe('SJ00000202') }finally{await rm(dir,{recursive:true,force:true})} },20000) it('默认每页五条,分页互不重复,按筛选结果计数,并在删除末页记录后回到有效页',async()=>{ const dir=await mkdtemp(path.join(tmpdir(),'receipt-pages-')) try{ const ledger=new Ledger(path.join(dir,'db.sqlite'));await ledger.open() const batches=[] for(let i=1;i<=12;i++){ const batch=await ledger.reserve({name:'批次'+i,hash:String(i)},[input(i%2?'甲学校':'乙学校','高一','学生'+i,'服务费')],defaultSettings(dir),null) batch.status='completed';await ledger.update(batch);batches.push(batch) } const first=ledger.batchPage(),second=ledger.batchPage({page:2}),last=ledger.batchPage({page:3}) expect(first).toMatchObject({total:12,page:1,pageSize:5}) expect(first.items.map(batch=>batch.sourceName)).toEqual(['批次12','批次11','批次10','批次9','批次8']) expect(second.items).toHaveLength(5);expect(last.items).toHaveLength(2) expect(new Set([...first.items,...second.items,...last.items].map(batch=>batch.id)).size).toBe(12) expect(ledger.batchPage({page:2,pageSize:10})).toMatchObject({total:12,page:2,pageSize:10,items:expect.arrayContaining([{...last.items[0]},{...last.items[1]}])}) expect(ledger.batchPage({pageSize:50}).items).toHaveLength(12) expect(ledger.batchPage({school:'甲学校',page:2})).toMatchObject({total:6,page:2,items:[{id:batches[0].id}]}) expect(ledger.batchPage({school:'甲学校',page:999})).toMatchObject({total:6,page:2,items:[{id:batches[0].id}]}) expect(ledger.batchPage({search:'不存在',page:999})).toEqual({items:[],total:0,page:1,pageSize:5}) await ledger.deleteBatch(batches[0].id);await ledger.deleteBatch(batches[1].id) expect(ledger.batchPage({page:3})).toMatchObject({total:10,page:2,items:expect.any(Array)}) expect(ledger.batchPage({page:3}).items).toHaveLength(5) expect(ledger.nextNumber('SJ')).toBe('SJ00000013') }finally{await rm(dir,{recursive:true,force:true})} })