feat(CC-066): add ScenariosController (CRUD + decision grouping)
This commit is contained in:
parent
a8d8e4fc32
commit
988a7db958
1 changed files with 90 additions and 0 deletions
90
src/scenarios/scenarios.controller.ts
Normal file
90
src/scenarios/scenarios.controller.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { Body, Controller, Delete, Get, Param, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common';
|
||||
import { IsIn, IsNumberString, IsOptional, IsString, IsUUID } from 'class-validator';
|
||||
import { and, desc, eq } from 'drizzle-orm';
|
||||
import { CurrentSession } from '../auth/session.decorator';
|
||||
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
||||
import { db } from '../db/client';
|
||||
import { scenarios } from '../db/schema';
|
||||
|
||||
class CreateScenarioDto {
|
||||
@IsString() title!: string;
|
||||
@IsOptional() @IsString() description?: string;
|
||||
@IsOptional() @IsUUID() decisionId?: string;
|
||||
@IsOptional() @IsIn(['low','medium','high']) probability?: string;
|
||||
@IsOptional() @IsString() outcome?: string;
|
||||
@IsOptional() @IsNumberString() financialImpactMinorUnits?: string;
|
||||
@IsOptional() @IsString() financialImpactCurrency?: string;
|
||||
@IsOptional() @IsIn(['positive','negative','neutral']) impactDirection?: string;
|
||||
@IsOptional() @IsIn(['hypothetical','likely','confirmed','ruled_out']) status?: string;
|
||||
}
|
||||
|
||||
class UpdateScenarioDto {
|
||||
@IsOptional() @IsString() title?: string;
|
||||
@IsOptional() @IsString() description?: string;
|
||||
@IsOptional() @IsIn(['low','medium','high']) probability?: string;
|
||||
@IsOptional() @IsString() outcome?: string;
|
||||
@IsOptional() @IsNumberString() financialImpactMinorUnits?: string;
|
||||
@IsOptional() @IsIn(['positive','negative','neutral']) impactDirection?: string;
|
||||
@IsOptional() @IsIn(['hypothetical','likely','confirmed','ruled_out']) status?: string;
|
||||
}
|
||||
|
||||
@Controller('scenarios')
|
||||
export class ScenariosController {
|
||||
@Get()
|
||||
async list(
|
||||
@CurrentSession() session: AuthenticatedSession,
|
||||
@Query('decisionId') decisionId?: string,
|
||||
@Query('status') status?: string,
|
||||
) {
|
||||
const tid = session.tenantId;
|
||||
let where = eq(scenarios.tenantId, tid) as ReturnType<typeof eq>;
|
||||
if (decisionId) where = and(where, eq(scenarios.decisionId, decisionId)) as typeof where;
|
||||
if (status) where = and(where, eq(scenarios.status, status)) as typeof where;
|
||||
return db.query.scenarios.findMany({ where, orderBy: [desc(scenarios.createdAt)], limit: 500 });
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateScenarioDto) {
|
||||
const [row] = await db.insert(scenarios).values({
|
||||
tenantId: session.tenantId,
|
||||
title: dto.title,
|
||||
description: dto.description,
|
||||
decisionId: dto.decisionId ?? null,
|
||||
probability: dto.probability ?? 'medium',
|
||||
outcome: dto.outcome,
|
||||
financialImpactMinorUnits: dto.financialImpactMinorUnits ?? null,
|
||||
financialImpactCurrency: dto.financialImpactCurrency ?? 'RON',
|
||||
impactDirection: dto.impactDirection ?? 'neutral',
|
||||
status: dto.status ?? 'hypothetical',
|
||||
}).returning();
|
||||
return row;
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@CurrentSession() session: AuthenticatedSession,
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() dto: UpdateScenarioDto,
|
||||
) {
|
||||
const updates: Record<string, unknown> = { updatedAt: new Date() };
|
||||
if (dto.title !== undefined) updates.title = dto.title;
|
||||
if (dto.description !== undefined) updates.description = dto.description;
|
||||
if (dto.probability !== undefined) updates.probability = dto.probability;
|
||||
if (dto.outcome !== undefined) updates.outcome = dto.outcome;
|
||||
if (dto.financialImpactMinorUnits !== undefined) updates.financialImpactMinorUnits = dto.financialImpactMinorUnits;
|
||||
if (dto.impactDirection !== undefined) updates.impactDirection = dto.impactDirection;
|
||||
if (dto.status !== undefined) updates.status = dto.status;
|
||||
const [row] = await db.update(scenarios)
|
||||
.set(updates)
|
||||
.where(and(eq(scenarios.id, id), eq(scenarios.tenantId, session.tenantId)))
|
||||
.returning();
|
||||
return row;
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async remove(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) {
|
||||
await db.delete(scenarios)
|
||||
.where(and(eq(scenarios.id, id), eq(scenarios.tenantId, session.tenantId)));
|
||||
return { deleted: true };
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue