From b60c255080b6498f39de89847a92c36150869a94 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Fri, 31 Jul 2026 10:44:54 +0000 Subject: [PATCH] feat(decisions): add decisions.service --- src/decisions/decisions.service.ts | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/decisions/decisions.service.ts diff --git a/src/decisions/decisions.service.ts b/src/decisions/decisions.service.ts new file mode 100644 index 0000000..911a1a2 --- /dev/null +++ b/src/decisions/decisions.service.ts @@ -0,0 +1,74 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { and, desc, eq } from 'drizzle-orm'; +import { db } from '../db/client'; +import { decisions } from '../db/schema'; +import { AuditService } from '../audit/audit.service'; +import type { AuthenticatedSession } from '../auth/tenant.guard'; +import type { CreateDecisionDto, UpdateDecisionDto } from './dto'; + +@Injectable() +export class DecisionsService { + constructor(private readonly audit: AuditService) {} + + async list(session: AuthenticatedSession) { + return db.query.decisions.findMany({ + where: eq(decisions.tenantId, session.tenantId), + orderBy: desc(decisions.createdAt), + }); + } + + async getById(session: AuthenticatedSession, id: string) { + const row = await db.query.decisions.findFirst({ + where: and(eq(decisions.id, id), eq(decisions.tenantId, session.tenantId)), + }); + if (!row) throw new NotFoundException('Decision not found'); + return row; + } + + async create(session: AuthenticatedSession, dto: CreateDecisionDto) { + const [row] = await db + .insert(decisions) + .values({ + tenantId: session.tenantId, + ownerUserId: session.userId, + context: dto.context, + options: (dto.options ?? []) as unknown[], + assumptions: (dto.assumptions ?? []) as unknown[], + evidence: (dto.evidence ?? []) as unknown[], + reviewDueAt: dto.reviewDueAt ? new Date(dto.reviewDueAt) : undefined, + }) + .returning(); + await this.audit.record(null, { + tenantId: session.tenantId, + actorId: session.userId, + action: 'decision.created', + resource: `decision:${row.id}`, + }); + return row; + } + + async update(session: AuthenticatedSession, id: string, dto: UpdateDecisionDto) { + await this.getById(session, id); + const [row] = await db + .update(decisions) + .set({ + ...(dto.context !== undefined ? { context: dto.context } : {}), + ...(dto.options !== undefined ? { options: dto.options as unknown[] } : {}), + ...(dto.assumptions !== undefined ? { assumptions: dto.assumptions as unknown[] } : {}), + ...(dto.evidence !== undefined ? { evidence: dto.evidence as unknown[] } : {}), + ...(dto.selectedOption !== undefined ? { selectedOption: dto.selectedOption } : {}), + ...(dto.outcomeReview !== undefined ? { outcomeReview: dto.outcomeReview } : {}), + ...(dto.reviewDueAt !== undefined ? { reviewDueAt: new Date(dto.reviewDueAt) } : {}), + updatedAt: new Date(), + }) + .where(and(eq(decisions.id, id), eq(decisions.tenantId, session.tenantId))) + .returning(); + await this.audit.record(null, { + tenantId: session.tenantId, + actorId: session.userId, + action: dto.selectedOption !== undefined ? 'decision.option_selected' : 'decision.updated', + resource: `decision:${id}`, + }); + return row; + } +}