feat(#119): GET /dossiers/:numeroDossier unifié AM ou famille (type + dossier)
Made-with: Cursor
This commit is contained in:
parent
f6fabc521e
commit
6e2343087e
@ -17,6 +17,7 @@ import { EnfantsModule } from './routes/enfants/enfants.module';
|
||||
import { AppConfigModule } from './modules/config/config.module';
|
||||
import { DocumentsLegauxModule } from './modules/documents-legaux';
|
||||
import { RelaisModule } from './routes/relais/relais.module';
|
||||
import { DossiersModule } from './routes/dossiers/dossiers.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -55,6 +56,7 @@ import { RelaisModule } from './routes/relais/relais.module';
|
||||
AppConfigModule,
|
||||
DocumentsLegauxModule,
|
||||
RelaisModule,
|
||||
DossiersModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [
|
||||
|
||||
26
backend/src/routes/dossiers/dossiers.controller.ts
Normal file
26
backend/src/routes/dossiers/dossiers.controller.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
|
||||
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { Roles } from 'src/common/decorators/roles.decorator';
|
||||
import { RoleType } from 'src/entities/users.entity';
|
||||
import { AuthGuard } from 'src/common/guards/auth.guard';
|
||||
import { RolesGuard } from 'src/common/guards/roles.guard';
|
||||
import { DossiersService } from './dossiers.service';
|
||||
import { DossierUnifieDto } from './dto/dossier-unifie.dto';
|
||||
|
||||
@ApiTags('Dossiers')
|
||||
@Controller('dossiers')
|
||||
@UseGuards(AuthGuard, RolesGuard)
|
||||
export class DossiersController {
|
||||
constructor(private readonly dossiersService: DossiersService) {}
|
||||
|
||||
@Get(':numeroDossier')
|
||||
@Roles(RoleType.SUPER_ADMIN, RoleType.ADMINISTRATEUR, RoleType.GESTIONNAIRE)
|
||||
@ApiOperation({ summary: 'Dossier complet par numéro (AM ou famille) – Ticket #119' })
|
||||
@ApiParam({ name: 'numeroDossier', description: 'Numéro de dossier (ex: 2026-000001)' })
|
||||
@ApiResponse({ status: 200, description: 'Dossier famille ou AM', type: DossierUnifieDto })
|
||||
@ApiResponse({ status: 404, description: 'Aucun dossier pour ce numéro' })
|
||||
@ApiResponse({ status: 403, description: 'Accès refusé' })
|
||||
getDossier(@Param('numeroDossier') numeroDossier: string): Promise<DossierUnifieDto> {
|
||||
return this.dossiersService.getDossierByNumero(numeroDossier);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,28 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { Parents } from 'src/entities/parents.entity';
|
||||
import { AssistanteMaternelle } from 'src/entities/assistantes_maternelles.entity';
|
||||
import { ParentsModule } from '../parents/parents.module';
|
||||
import { DossiersController } from './dossiers.controller';
|
||||
import { DossiersService } from './dossiers.service';
|
||||
|
||||
@Module({})
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Parents, AssistanteMaternelle]),
|
||||
ParentsModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get('jwt.accessSecret'),
|
||||
signOptions: { expiresIn: config.get('jwt.accessExpiresIn') },
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
],
|
||||
controllers: [DossiersController],
|
||||
providers: [DossiersService],
|
||||
exports: [DossiersService],
|
||||
})
|
||||
export class DossiersModule {}
|
||||
|
||||
81
backend/src/routes/dossiers/dossiers.service.ts
Normal file
81
backend/src/routes/dossiers/dossiers.service.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Parents } from 'src/entities/parents.entity';
|
||||
import { AssistanteMaternelle } from 'src/entities/assistantes_maternelles.entity';
|
||||
import { ParentsService } from '../parents/parents.service';
|
||||
import { DossierUnifieDto } from './dto/dossier-unifie.dto';
|
||||
import { DossierAmCompletDto, DossierAmUserDto } from './dto/dossier-am-complet.dto';
|
||||
|
||||
/**
|
||||
* Endpoint unifié GET /dossiers/:numeroDossier – AM ou famille. Ticket #119.
|
||||
*/
|
||||
@Injectable()
|
||||
export class DossiersService {
|
||||
constructor(
|
||||
@InjectRepository(Parents)
|
||||
private readonly parentsRepository: Repository<Parents>,
|
||||
@InjectRepository(AssistanteMaternelle)
|
||||
private readonly amRepository: Repository<AssistanteMaternelle>,
|
||||
private readonly parentsService: ParentsService,
|
||||
) {}
|
||||
|
||||
async getDossierByNumero(numeroDossier: string): Promise<DossierUnifieDto> {
|
||||
const num = numeroDossier?.trim();
|
||||
if (!num) {
|
||||
throw new NotFoundException('Numéro de dossier requis.');
|
||||
}
|
||||
|
||||
// 1) Famille : un parent a ce numéro ?
|
||||
const parentWithNum = await this.parentsRepository.findOne({
|
||||
where: { numero_dossier: num },
|
||||
select: ['user_id'],
|
||||
});
|
||||
if (parentWithNum) {
|
||||
const dossier = await this.parentsService.getDossierFamilleByNumero(num);
|
||||
return { type: 'family', dossier };
|
||||
}
|
||||
|
||||
// 2) AM : une assistante maternelle a ce numéro ?
|
||||
const am = await this.amRepository.findOne({
|
||||
where: { numero_dossier: num },
|
||||
relations: ['user'],
|
||||
});
|
||||
if (am?.user) {
|
||||
const dossier: DossierAmCompletDto = {
|
||||
numero_dossier: num,
|
||||
user: this.toDossierAmUserDto(am.user),
|
||||
numero_agrement: am.approval_number,
|
||||
nir: am.nir,
|
||||
biographie: am.biography,
|
||||
disponible: am.available,
|
||||
ville_residence: am.residence_city,
|
||||
date_agrement: am.agreement_date,
|
||||
annees_experience: am.years_experience,
|
||||
specialite: am.specialty,
|
||||
nb_max_enfants: am.max_children,
|
||||
place_disponible: am.places_available,
|
||||
};
|
||||
return { type: 'am', dossier };
|
||||
}
|
||||
|
||||
throw new NotFoundException('Aucun dossier trouvé pour ce numéro.');
|
||||
}
|
||||
|
||||
private toDossierAmUserDto(user: { id: string; email: string; prenom?: string; nom?: string; telephone?: string; adresse?: string; ville?: string; code_postal?: string; profession?: string; date_naissance?: Date; photo_url?: string; statut: any }): DossierAmUserDto {
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
prenom: user.prenom,
|
||||
nom: user.nom,
|
||||
telephone: user.telephone,
|
||||
adresse: user.adresse,
|
||||
ville: user.ville,
|
||||
code_postal: user.code_postal,
|
||||
profession: user.profession,
|
||||
date_naissance: user.date_naissance,
|
||||
photo_url: user.photo_url,
|
||||
statut: user.statut,
|
||||
};
|
||||
}
|
||||
}
|
||||
58
backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts
Normal file
58
backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { StatutUtilisateurType } from 'src/entities/users.entity';
|
||||
|
||||
/** Utilisateur AM sans données sensibles (pour dossier AM complet). Ticket #119 */
|
||||
export class DossierAmUserDto {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
@ApiProperty()
|
||||
email: string;
|
||||
@ApiProperty({ required: false })
|
||||
prenom?: string;
|
||||
@ApiProperty({ required: false })
|
||||
nom?: string;
|
||||
@ApiProperty({ required: false })
|
||||
telephone?: string;
|
||||
@ApiProperty({ required: false })
|
||||
adresse?: string;
|
||||
@ApiProperty({ required: false })
|
||||
ville?: string;
|
||||
@ApiProperty({ required: false })
|
||||
code_postal?: string;
|
||||
@ApiProperty({ required: false })
|
||||
profession?: string;
|
||||
@ApiProperty({ required: false })
|
||||
date_naissance?: Date;
|
||||
@ApiProperty({ required: false })
|
||||
photo_url?: string;
|
||||
@ApiProperty({ enum: StatutUtilisateurType })
|
||||
statut: StatutUtilisateurType;
|
||||
}
|
||||
|
||||
/** Dossier AM complet (fiche AM sans secrets). Ticket #119 */
|
||||
export class DossierAmCompletDto {
|
||||
@ApiProperty({ example: '2026-000003', description: 'Numéro de dossier AM' })
|
||||
numero_dossier: string;
|
||||
@ApiProperty({ type: DossierAmUserDto, description: 'Utilisateur (sans mot de passe ni tokens)' })
|
||||
user: DossierAmUserDto;
|
||||
@ApiProperty({ required: false })
|
||||
numero_agrement?: string;
|
||||
@ApiProperty({ required: false })
|
||||
nir?: string;
|
||||
@ApiProperty({ required: false })
|
||||
biographie?: string;
|
||||
@ApiProperty({ required: false })
|
||||
disponible?: boolean;
|
||||
@ApiProperty({ required: false })
|
||||
ville_residence?: string;
|
||||
@ApiProperty({ required: false })
|
||||
date_agrement?: Date;
|
||||
@ApiProperty({ required: false })
|
||||
annees_experience?: number;
|
||||
@ApiProperty({ required: false })
|
||||
specialite?: string;
|
||||
@ApiProperty({ required: false })
|
||||
nb_max_enfants?: number;
|
||||
@ApiProperty({ required: false })
|
||||
place_disponible?: number;
|
||||
}
|
||||
14
backend/src/routes/dossiers/dto/dossier-unifie.dto.ts
Normal file
14
backend/src/routes/dossiers/dto/dossier-unifie.dto.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { DossierFamilleCompletDto } from '../../parents/dto/dossier-famille-complet.dto';
|
||||
import { DossierAmCompletDto } from './dossier-am-complet.dto';
|
||||
|
||||
/** Réponse unifiée GET /dossiers/:numeroDossier – AM ou famille. Ticket #119 */
|
||||
export class DossierUnifieDto {
|
||||
@ApiProperty({ enum: ['family', 'am'], description: 'Type de dossier' })
|
||||
type: 'family' | 'am';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Dossier famille (si type=family) ou dossier AM (si type=am)',
|
||||
})
|
||||
dossier: DossierFamilleCompletDto | DossierAmCompletDto;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user