mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
refactor: ( iam ) remove user relations
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- 1. Add iamUserId to Agent
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
ALTER TABLE passenger."Agent" ADD COLUMN IF NOT EXISTS "iamUserId" TEXT;
|
||||
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'Agent_iamUserId_key'
|
||||
AND conrelid = 'passenger."Agent"'::regclass
|
||||
) THEN
|
||||
ALTER TABLE passenger."Agent" ADD CONSTRAINT "Agent_iamUserId_key" UNIQUE ("iamUserId");
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "Agent_iamUserId_idx" ON passenger."Agent"("iamUserId");
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- 2. Populate iamUserId for existing agent records
|
||||
-- Match via User.email → iam.users.email
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
UPDATE passenger."Agent" a
|
||||
SET "iamUserId" = iu.id
|
||||
FROM passenger."User" u
|
||||
JOIN iam.users iu ON iu.email = u.email
|
||||
WHERE a."userId" = u.id
|
||||
AND a."iamUserId" IS NULL;
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- 3. Drop Agent.userId FK and column — iamUserId replaces it entirely
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
ALTER TABLE passenger."Agent" DROP CONSTRAINT IF EXISTS "Agent_userId_fkey";
|
||||
DROP INDEX IF EXISTS passenger."Agent_userId_key";
|
||||
ALTER TABLE passenger."Agent" DROP COLUMN IF EXISTS "userId";
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- 4. Drop Passenger.userId FK (column stays as plain nullable string)
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
ALTER TABLE passenger."Passenger" DROP CONSTRAINT IF EXISTS "Passenger_userId_fkey";
|
||||
@@ -260,8 +260,6 @@ model User {
|
||||
faydaVerifiedAt DateTime?
|
||||
faydaSub String? @unique
|
||||
|
||||
passenger Passenger?
|
||||
agent Agent?
|
||||
sessions Session[]
|
||||
|
||||
@@schema("passenger")
|
||||
@@ -288,7 +286,6 @@ model Passenger {
|
||||
preferredLanguage String?
|
||||
blockedUntil DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
bookings Booking[]
|
||||
loyalty LoyaltyAccount?
|
||||
wallet WalletAccount?
|
||||
@@ -1057,16 +1054,16 @@ model SegmentFareRule {
|
||||
|
||||
model Agent {
|
||||
id String @id @default(uuid())
|
||||
userId String @unique
|
||||
iamUserId String? @unique
|
||||
agentCode String @unique
|
||||
stationId String?
|
||||
commissionRate Int @default(5)
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
bookings AgentBooking[]
|
||||
shifts AgentShift[]
|
||||
commissions AgentCommission[]
|
||||
@@index([iamUserId])
|
||||
@@schema("passenger")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
// Thin adapter: re-exports IAM guard and a stub IamRoles decorator so that
|
||||
// controllers written against the forthcoming IamGuard compile today.
|
||||
export { JwtGuard as IamGuard } from '@tria-plc/api-common/modules/auth/services/jwt.guard';
|
||||
|
||||
export function IamRoles(..._roles: string[]) {
|
||||
return function (_target: any, _key?: any, _descriptor?: any) {};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import { UserRole } from '@prisma/client';
|
||||
|
||||
export const ROLES_KEY = 'roles';
|
||||
export const Roles = (...roles: UserRole[]) => SetMetadata(ROLES_KEY, roles);
|
||||
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
|
||||
|
||||
@@ -13,9 +13,13 @@ export class AgentsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async createAgentBooking(dto: CreateAgentBookingDto) {
|
||||
const agent = await this.prisma.agent.findUnique({ where: { id: dto.agentId }, include: { user: { include: { passenger: true } } } });
|
||||
const agent = await this.prisma.agent.findUnique({ where: { id: dto.agentId } });
|
||||
if (!agent || !agent.active) throw new NotFoundException('Agent not found or inactive');
|
||||
if (!agent.user.passenger) throw new BadRequestException('Agent must have passenger account');
|
||||
|
||||
const passenger = agent.iamUserId
|
||||
? await this.prisma.passenger.findUnique({ where: { iamUserId: agent.iamUserId } })
|
||||
: null;
|
||||
if (!passenger) throw new BadRequestException('Agent must have a linked passenger account');
|
||||
|
||||
const schedule = await this.prisma.trainSchedule.findUnique({ where: { id: dto.scheduleId } });
|
||||
if (!schedule) throw new NotFoundException('Schedule not found');
|
||||
@@ -30,7 +34,7 @@ export class AgentsService {
|
||||
const booking = await this.prisma.booking.create({
|
||||
data: {
|
||||
bookingRef: generateRef(),
|
||||
passengerId: agent.user.passenger.id,
|
||||
passengerId: passenger.id,
|
||||
scheduleId: dto.scheduleId,
|
||||
status: dto.paymentMethod === 'CASH' ? 'CONFIRMED' : 'PENDING_PAYMENT',
|
||||
totalMinor,
|
||||
|
||||
@@ -2,7 +2,9 @@ import { Controller, Get, Post, Patch, Delete, Body, Param, HttpCode, UseGuards
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { CurrenciesService } from './currencies.service';
|
||||
import { CreateCurrencyDto, UpdateCurrencyDto } from './currencies.dto';
|
||||
import { IamGuard, IamRoles } from '../../common/iam-adapter';
|
||||
import { IamGuard } from '../../common/iam-adapter';
|
||||
import { Roles } from '../../common/roles.decorator';
|
||||
import { RolesGuard } from '../../common/roles.guard';
|
||||
|
||||
@ApiTags('Currencies')
|
||||
@Controller('currencies')
|
||||
@@ -15,8 +17,8 @@ export class CurrenciesController {
|
||||
}
|
||||
|
||||
@Post()
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN')
|
||||
@ApiBearerAuth('IAM-auth')
|
||||
@HttpCode(201)
|
||||
createCurrency(@Body() dto: CreateCurrencyDto) {
|
||||
@@ -24,24 +26,24 @@ export class CurrenciesController {
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN')
|
||||
@ApiBearerAuth('IAM-auth')
|
||||
updateCurrency(@Param('id') id: string, @Body() dto: UpdateCurrencyDto) {
|
||||
return this.currenciesService.updateCurrency(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN')
|
||||
@ApiBearerAuth('IAM-auth')
|
||||
deleteCurrency(@Param('id') id: string) {
|
||||
return this.currenciesService.deleteCurrency(id);
|
||||
}
|
||||
|
||||
@Post('sync-rates')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN')
|
||||
@ApiBearerAuth('IAM-auth')
|
||||
@HttpCode(200)
|
||||
syncRates() {
|
||||
|
||||
@@ -2,7 +2,9 @@ import { Controller, Get, Param, Patch, Post, Body, UseGuards } from '@nestjs/co
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody } from '@nestjs/swagger';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import { JwtGuard } from '../../common/jwt.guard';
|
||||
import { IamGuard, IamRoles } from '../../common/iam-adapter';
|
||||
import { IamGuard } from '../../common/iam-adapter';
|
||||
import { Roles } from '../../common/roles.decorator';
|
||||
import { RolesGuard } from '../../common/roles.guard';
|
||||
import { TestNotificationDto } from './notifications.dto';
|
||||
import { EmailClientService } from './email-client.service';
|
||||
import { SmsClientService } from './sms-client.service';
|
||||
@@ -39,8 +41,8 @@ export class NotificationsController {
|
||||
}
|
||||
|
||||
@Post('send/email')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN', 'STAFF')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN', 'STAFF')
|
||||
@ApiOperation({ summary: 'Send a direct email via the email microservice' })
|
||||
@ApiBody({ type: SendEmail })
|
||||
sendEmail(@Body() dto: SendEmail) {
|
||||
@@ -48,8 +50,8 @@ export class NotificationsController {
|
||||
}
|
||||
|
||||
@Post('send/sms')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN', 'STAFF')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN', 'STAFF')
|
||||
@ApiOperation({ summary: 'Send a direct SMS via the SMS microservice' })
|
||||
@ApiBody({ type: SingleMessageDto })
|
||||
sendSms(@Body() dto: SingleMessageDto) {
|
||||
@@ -57,8 +59,8 @@ export class NotificationsController {
|
||||
}
|
||||
|
||||
@Post('send/sms/bulk')
|
||||
@UseGuards(IamGuard)
|
||||
@IamRoles('ADMIN', 'STAFF')
|
||||
@UseGuards(IamGuard, RolesGuard)
|
||||
@Roles('ADMIN', 'STAFF')
|
||||
@ApiOperation({ summary: 'Send bulk SMS messages via the SMS microservice' })
|
||||
@ApiBody({ type: BulkMessagesDto })
|
||||
sendBulkSms(@Body() dto: BulkMessagesDto) {
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
import { JwtGuard } from "../../common/jwt.guard";
|
||||
import { RolesGuard } from "../../common/roles.guard";
|
||||
import { Roles } from "../../common/roles.decorator";
|
||||
import { UserRole } from "@prisma/client";
|
||||
|
||||
@ApiTags("Payment")
|
||||
@Controller("payments")
|
||||
@@ -40,7 +39,7 @@ export class PaymentsController {
|
||||
|
||||
@Get("all")
|
||||
@UseGuards(JwtGuard, RolesGuard)
|
||||
@Roles(UserRole.ADMIN, UserRole.SUPERVISOR, UserRole.STAFF)
|
||||
@Roles('ADMIN', 'SUPERVISOR', 'STAFF')
|
||||
@ApiBearerAuth("JWT-auth")
|
||||
@ApiOperation({ summary: "Get all payments with filters (staff/admin only)" })
|
||||
@ApiQuery({ name: "search", required: false })
|
||||
@@ -103,7 +102,7 @@ export class PaymentsController {
|
||||
|
||||
@Post("refund")
|
||||
@UseGuards(JwtGuard, RolesGuard)
|
||||
@Roles(UserRole.ADMIN, UserRole.STAFF, UserRole.AGENT)
|
||||
@Roles('ADMIN', 'STAFF', 'AGENT')
|
||||
@ApiBearerAuth("JWT-auth")
|
||||
@ApiOperation({ summary: "Refund a confirmed booking (staff/agent only)" })
|
||||
refund(@Body() dto: RefundDto) {
|
||||
@@ -112,7 +111,7 @@ export class PaymentsController {
|
||||
|
||||
@Post("methods")
|
||||
@UseGuards(JwtGuard, RolesGuard)
|
||||
@Roles(UserRole.ADMIN, UserRole.STAFF)
|
||||
@Roles('ADMIN', 'STAFF')
|
||||
@ApiBearerAuth("JWT-auth")
|
||||
@ApiOperation({
|
||||
summary: "Add a payment system to the platform catalog (admin only)",
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { GenerateReportDto, ReportType } from './reports.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ReportsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
@InjectDataSource() private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async generateReport(dto: GenerateReportDto) {
|
||||
const dateFrom = new Date(dto.dateFrom);
|
||||
@@ -113,13 +118,25 @@ export class ReportsService {
|
||||
...(agentId ? { agentId } : {})
|
||||
},
|
||||
include: {
|
||||
agent: { include: { user: true } },
|
||||
agent: { select: { id: true, iamUserId: true, agentCode: true } },
|
||||
booking: true
|
||||
}
|
||||
});
|
||||
|
||||
const iamUserIds = [...new Set(
|
||||
agentBookings.map(ab => ab.agent.iamUserId).filter(Boolean) as string[]
|
||||
)];
|
||||
const iamRows = iamUserIds.length > 0
|
||||
? await this.dataSource.query<{ id: string; name: { en?: string; am?: string } | null }[]>(
|
||||
`SELECT id, name FROM iam.users WHERE id = ANY($1)`,
|
||||
[iamUserIds],
|
||||
)
|
||||
: [];
|
||||
const iamMap = new Map(iamRows.map(r => [r.id, r]));
|
||||
|
||||
const byAgent = agentBookings.reduce((acc, ab) => {
|
||||
const agentName = ab.agent.user.fullName;
|
||||
const iam = ab.agent.iamUserId ? iamMap.get(ab.agent.iamUserId) : undefined;
|
||||
const agentName = iam?.name?.en ?? iam?.name?.am ?? ab.agent.agentCode;
|
||||
if (!acc[agentName]) {
|
||||
acc[agentName] = { bookings: 0, revenueMinor: 0, cashCollected: 0 };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user