fix(c3): 垫付结转 R9 门控——仅待遇已转正常享受(active)可结转

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-15 00:49:24 +08:00
parent 1966a68bc8
commit 169b4e2c51
2 changed files with 22 additions and 1 deletions

View File

@ -21,6 +21,7 @@ function makeService(
orderFindUnique?: ReturnType<typeof vi.fn>; orderFindUnique?: ReturnType<typeof vi.fn>;
staffFindFirst?: ReturnType<typeof vi.fn>; staffFindFirst?: ReturnType<typeof vi.fn>;
elderFindFirst?: ReturnType<typeof vi.fn>; elderFindFirst?: ReturnType<typeof vi.fn>;
elderFindUnique?: ReturnType<typeof vi.fn>;
recordFindUnique?: ReturnType<typeof vi.fn>; recordFindUnique?: ReturnType<typeof vi.fn>;
recordFindFirst?: ReturnType<typeof vi.fn>; recordFindFirst?: ReturnType<typeof vi.fn>;
recordFindMany?: ReturnType<typeof vi.fn>; recordFindMany?: ReturnType<typeof vi.fn>;
@ -40,6 +41,7 @@ function makeService(
const orderFindUnique = overrides.orderFindUnique ?? vi.fn().mockResolvedValue(null); const orderFindUnique = overrides.orderFindUnique ?? vi.fn().mockResolvedValue(null);
const staffFindFirst = overrides.staffFindFirst ?? vi.fn().mockResolvedValue(null); const staffFindFirst = overrides.staffFindFirst ?? vi.fn().mockResolvedValue(null);
const elderFindFirst = overrides.elderFindFirst ?? 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 recordFindUnique = overrides.recordFindUnique ?? vi.fn().mockResolvedValue(null);
const recordFindFirst = overrides.recordFindFirst ?? vi.fn().mockResolvedValue(null); const recordFindFirst = overrides.recordFindFirst ?? vi.fn().mockResolvedValue(null);
const recordFindMany = overrides.recordFindMany ?? vi.fn().mockResolvedValue([]); const recordFindMany = overrides.recordFindMany ?? vi.fn().mockResolvedValue([]);
@ -59,7 +61,7 @@ function makeService(
client: { client: {
order: { findUnique: orderFindUnique }, order: { findUnique: orderFindUnique },
staff: { findFirst: staffFindFirst }, staff: { findFirst: staffFindFirst },
elder: { findFirst: elderFindFirst }, elder: { findFirst: elderFindFirst, findUnique: elderFindUnique },
record: { record: {
findUnique: recordFindUnique, findUnique: recordFindUnique,
findFirst: recordFindFirst, findFirst: recordFindFirst,
@ -88,6 +90,7 @@ function makeService(
orderFindUnique, orderFindUnique,
staffFindFirst, staffFindFirst,
elderFindFirst, elderFindFirst,
elderFindUnique,
recordFindUnique, recordFindUnique,
recordFindFirst, recordFindFirst,
recordFindMany, 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 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 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 recordCreate = vi.fn().mockResolvedValue({ id: 'r2' });
const elderFindUnique = vi.fn().mockResolvedValue({ id: 'e1', treatStatus: 'active' });
const { service, recordItemCreate, recordPhotoCreate, recordUpdate } = makeService({ const { service, recordItemCreate, recordPhotoCreate, recordUpdate } = makeService({
recordFindMany, recordFindMany,
recordItemFindMany, recordItemFindMany,
recordPhotoFindMany, recordPhotoFindMany,
recordCreate, recordCreate,
elderFindUnique,
}); });
const res = await runWithRequestContext(ctx, () => service.prepaidCarryover({ recordIds: ['r1'] })); const res = await runWithRequestContext(ctx, () => service.prepaidCarryover({ recordIds: ['r1'] }));
@ -611,6 +616,17 @@ describe('RecordService.prepaidCarryover', () => {
).rejects.toMatchObject({ response: { code: ErrorCodes.PREPAID_CARRYOVER_INVALID } }); ).rejects.toMatchObject({ response: { code: ErrorCodes.PREPAID_CARRYOVER_INVALID } });
expect(recordCreate).not.toHaveBeenCalled(); 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', () => { describe('RecordService.detail / list / prepaidList', () => {

View File

@ -304,6 +304,11 @@ export class RecordService {
if (records.some((r) => !r.isPrepaid)) { if (records.some((r) => !r.isPrepaid)) {
throw new BusinessException(ErrorCodes.PREPAID_CARRYOVER_INVALID, '仅垫付记录可结转', HttpStatus.BAD_REQUEST); 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 = []; const created = [];
for (const rec of records) { for (const rec of records) {