diff --git a/src/agency/agency.service.spec.ts b/src/agency/agency.service.spec.ts index 3cdec68..24892b8 100644 --- a/src/agency/agency.service.spec.ts +++ b/src/agency/agency.service.spec.ts @@ -55,6 +55,66 @@ describe('AgencyService', () => { data: { qcThreshold: 48, dockingTier: 'A', newNationalNotify: false }, }); }); + + it('无 agency 行时 PATCH 自动创建(最小默认值 + dto 字段,locCode 置空)', async () => { + const findUnique = vi.fn().mockResolvedValue(null); + const create = vi.fn().mockResolvedValue({ code: 'T001', orgName: '新机构' }); + const update = vi.fn(); + const prisma = { client: { agency: { findUnique, create, update } } }; + const service = new AgencyService(prisma as unknown as PrismaService); + + await runWithRequestContext(ctx, () => + service.update({ orgName: '新机构', qcThreshold: 48, dockingTier: 'A', newNationalNotify: false }), + ); + + expect(create).toHaveBeenCalledWith({ + data: { + code: 'T001', + orgName: '新机构', + orgType: '', + creditCode: '', + locCode: '', + locName: '', + svcSource: '', + address: '', + contactName: '', + contactPhone: '', + qcThreshold: 48, + dockingTier: 'A', + newNationalNotify: false, + }, + }); + expect(update).not.toHaveBeenCalled(); + }); + + it('无 agency 行且 dto 为空时,create 落最小默认值(24/B/true)', async () => { + const findUnique = vi.fn().mockResolvedValue(null); + const create = vi.fn().mockResolvedValue({ code: 'T001' }); + const update = vi.fn(); + const prisma = { client: { agency: { findUnique, create, update } } }; + const service = new AgencyService(prisma as unknown as PrismaService); + + await runWithRequestContext(ctx, () => service.update({})); + + expect(create).toHaveBeenCalledWith({ + data: { + code: 'T001', + orgName: '', + orgType: '', + creditCode: '', + locCode: '', + locName: '', + svcSource: '', + address: '', + contactName: '', + contactPhone: '', + qcThreshold: 24, + dockingTier: 'B', + newNationalNotify: true, + }, + }); + expect(update).not.toHaveBeenCalled(); + }); }); describe('UpdateAgencyDto.dockingTier', () => { diff --git a/src/agency/agency.service.ts b/src/agency/agency.service.ts index 338cf7d..0d3886b 100644 --- a/src/agency/agency.service.ts +++ b/src/agency/agency.service.ts @@ -1,10 +1,27 @@ -import { HttpStatus, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service.js'; import { getRequestContext } from '../common/context/request-context.js'; -import { ErrorCodes } from '../common/error-codes.js'; -import { BusinessException } from '../common/business-exception.js'; import { UpdateAgencyDto } from './dto.js'; +/** 无 agency 行时(R5:无 provisioning 路径)创建行的最小默认值。 */ +function agencyDefaults(code: string): Record { + return { + code, + orgName: '', + orgType: '', + creditCode: '', + locCode: '', + locName: '', + svcSource: '', + address: '', + contactName: '', + contactPhone: '', + qcThreshold: 24, + dockingTier: 'B', + newNationalNotify: true, + }; +} + /** * 机构(=租户)信息读写。Agency 是 TENANT_FREE 模型,主键 code = 当前租户 code, * 因此读写一律按 `ctx.tenantCode` 定位,而非 BaseCrudService 的 `findById(id)`。 @@ -19,13 +36,20 @@ export class AgencyService { return this.prisma.client.agency.findUnique({ where: { code: ctx.tenantCode } }); } - /** 更新当前机构信息;写操作同样按 code = ctx.tenantCode 定位,禁止按 id。 */ + /** + * 更新当前机构信息(upsert);写操作同样按 code = ctx.tenantCode 定位,禁止按 id。 + * R5:agency 无 provisioning 路径(空表 + 无 create),故无行时先建最小默认值行再落 dto 字段。 + * 参保地 locCode/locName 只读,不随 PATCH 更新(建行时置空串,dto 不含这两字段)。 + */ async update(dto: UpdateAgencyDto) { const ctx = getRequestContext()!; const existing = await this.getCurrent(); if (!existing) { - throw new BusinessException(ErrorCodes.ORG_NOT_FOUND, '机构不存在', HttpStatus.NOT_FOUND); + return this.prisma.client.agency.create({ + data: { ...agencyDefaults(ctx.tenantCode), ...dto } as any, + }); } return this.prisma.client.agency.update({ where: { code: ctx.tenantCode }, data: dto }); } } +