From 169b4e2c51e9fe1c9f0695a72e43b49748152798 Mon Sep 17 00:00:00 2001 From: liuyun Date: Thu, 15 Oct 2026 00:49:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(c3):=20=E5=9E=AB=E4=BB=98=E7=BB=93=E8=BD=AC?= =?UTF-8?q?=20R9=20=E9=97=A8=E6=8E=A7=E2=80=94=E2=80=94=E4=BB=85=E5=BE=85?= =?UTF-8?q?=E9=81=87=E5=B7=B2=E8=BD=AC=E6=AD=A3=E5=B8=B8=E4=BA=AB=E5=8F=97?= =?UTF-8?q?=EF=BC=88active=EF=BC=89=E5=8F=AF=E7=BB=93=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/record/record.service.spec.ts | 18 +++++++++++++++++- src/record/record.service.ts | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/record/record.service.spec.ts b/src/record/record.service.spec.ts index fe2b3c5..207015d 100644 --- a/src/record/record.service.spec.ts +++ b/src/record/record.service.spec.ts @@ -21,6 +21,7 @@ function makeService( orderFindUnique?: ReturnType; staffFindFirst?: ReturnType; elderFindFirst?: ReturnType; + elderFindUnique?: ReturnType; recordFindUnique?: ReturnType; recordFindFirst?: ReturnType; recordFindMany?: ReturnType; @@ -40,6 +41,7 @@ function makeService( const orderFindUnique = overrides.orderFindUnique ?? vi.fn().mockResolvedValue(null); const staffFindFirst = overrides.staffFindFirst ?? vi.fn().mockResolvedValue(null); const elderFindFirst = overrides.elderFindFirst ?? vi.fn().mockResolvedValue(null); + const elderFindUnique = overrides.elderFindUnique ?? vi.fn().mockResolvedValue(null); const recordFindUnique = overrides.recordFindUnique ?? vi.fn().mockResolvedValue(null); const recordFindFirst = overrides.recordFindFirst ?? vi.fn().mockResolvedValue(null); const recordFindMany = overrides.recordFindMany ?? vi.fn().mockResolvedValue([]); @@ -59,7 +61,7 @@ function makeService( client: { order: { findUnique: orderFindUnique }, staff: { findFirst: staffFindFirst }, - elder: { findFirst: elderFindFirst }, + elder: { findFirst: elderFindFirst, findUnique: elderFindUnique }, record: { findUnique: recordFindUnique, findFirst: recordFindFirst, @@ -88,6 +90,7 @@ function makeService( orderFindUnique, staffFindFirst, elderFindFirst, + elderFindUnique, recordFindUnique, recordFindFirst, recordFindMany, @@ -563,11 +566,13 @@ describe('RecordService.prepaidCarryover', () => { const recordItemFindMany = vi.fn().mockResolvedValue([{ id: 'ri1', svcId: 'svc1', svcNameSnapshot: '协助翻身', execResult: 'done', photoRequired: true, isRefusedPhoto: false, refuseReason: null, extraFlag: false, undoReason: null }]); const recordPhotoFindMany = vi.fn().mockResolvedValue([{ id: 'rp1', svcId: 'svc1', filePath: 'record/T001/a.jpg', fileSize: 100, visibleToFamily: false, keepUntil: null }]); const recordCreate = vi.fn().mockResolvedValue({ id: 'r2' }); + const elderFindUnique = vi.fn().mockResolvedValue({ id: 'e1', treatStatus: 'active' }); const { service, recordItemCreate, recordPhotoCreate, recordUpdate } = makeService({ recordFindMany, recordItemFindMany, recordPhotoFindMany, recordCreate, + elderFindUnique, }); const res = await runWithRequestContext(ctx, () => service.prepaidCarryover({ recordIds: ['r1'] })); @@ -611,6 +616,17 @@ describe('RecordService.prepaidCarryover', () => { ).rejects.toMatchObject({ response: { code: ErrorCodes.PREPAID_CARRYOVER_INVALID } }); expect(recordCreate).not.toHaveBeenCalled(); }); + + it('R9 门控:elder 仍 waiting_period → PREPAID_CARRYOVER_INVALID', async () => { + const recordFindMany = vi.fn().mockResolvedValue([original]); + const elderFindUnique = vi.fn().mockResolvedValue({ id: 'e1', treatStatus: 'waiting_period' }); + const { service, recordCreate } = makeService({ recordFindMany, elderFindUnique }); + + await expect( + runWithRequestContext(ctx, () => service.prepaidCarryover({ recordIds: ['r1'] })), + ).rejects.toMatchObject({ response: { code: ErrorCodes.PREPAID_CARRYOVER_INVALID } }); + expect(recordCreate).not.toHaveBeenCalled(); + }); }); describe('RecordService.detail / list / prepaidList', () => { diff --git a/src/record/record.service.ts b/src/record/record.service.ts index dee93ba..42bfa98 100644 --- a/src/record/record.service.ts +++ b/src/record/record.service.ts @@ -304,6 +304,11 @@ export class RecordService { if (records.some((r) => !r.isPrepaid)) { throw new BusinessException(ErrorCodes.PREPAID_CARRYOVER_INVALID, '仅垫付记录可结转', HttpStatus.BAD_REQUEST); } + // R9 门控:结转仅在待遇转「正常享受」后可用。 + const elder = await this.prisma.client.elder.findUnique({ where: { id: [...elderIds][0] } }); + if (!elder || elder.treatStatus !== 'active') { + throw new BusinessException(ErrorCodes.PREPAID_CARRYOVER_INVALID, '待遇未转正常享受,不可结转', HttpStatus.BAD_REQUEST); + } const created = []; for (const rec of records) {