Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { BackupModule } from './backup/backup.module';
import { TrackingModule } from './tracking/tracking.module';
import { NotificationsModule } from './notifications/notifications.module';
import { TransactionsModule } from './transactions/transactions.module';
import { EmailDigestModule } from './email-digest/email-digest.module';

@Module({
imports: [
ConfigModule.forRoot({
Expand Down Expand Up @@ -63,8 +63,8 @@ import { EmailDigestModule } from './email-digest/email-digest.module';
TrackingModule,
NotificationsModule,
TransactionsModule,
EmailDigestModule,
],

controllers: [AppController],
})
export class AppModule {}
73 changes: 73 additions & 0 deletions src/transactions/dto/transactions.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Type } from 'class-transformer';
import {
IsDate,
IsEnum,
IsInt,
IsOptional,
IsString,
IsUUID,
Max,
Min,
} from 'class-validator';
import { TransactionStatus, TransactionType } from '../../types/prisma.types';

export enum TransactionSortField {
CREATED_AT = 'createdAt',
AMOUNT = 'amount',
STATUS = 'status',
TYPE = 'type',
}

export enum SortOrder {
ASC = 'asc',
DESC = 'desc',
}

export class TransactionHistoryQueryDto {
@IsOptional()
@IsEnum(TransactionStatus)
status?: TransactionStatus;

@IsOptional()
@IsEnum(TransactionType)
type?: TransactionType;

@IsOptional()
@Type(() => Date)
@IsDate()
startDate?: Date;

@IsOptional()
@Type(() => Date)
@IsDate()
endDate?: Date;

@IsOptional()
@IsUUID('4')
propertyId?: string;

@IsOptional()
@IsUUID('4')
userId?: string;

@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page: number = 1;

@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit: number = 20;

@IsOptional()
@IsEnum(TransactionSortField)
sortBy: TransactionSortField = TransactionSortField.CREATED_AT;

@IsOptional()
@IsEnum(SortOrder)
sortOrder: SortOrder = SortOrder.DESC;
}
91 changes: 43 additions & 48 deletions src/transactions/transactions.controller.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,63 @@
import { Body, Controller, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import {
Controller,
Get,
Param,
Query,
UseGuards,
NotFoundException,
ForbiddenException,
} from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { AuthUserPayload } from '../auth/types/auth-user.type';
import { TransactionSearchQueryDto } from './dto/transaction-search.dto';
import {
CreateTransactionDto,
CreateTransactionTaxStrategyDto,
UpdateTransactionTaxStrategyDto,
} from './dto/transaction.dto';
import { UserRole } from '../types/prisma.types';
import { TransactionsService } from './transactions.service';
import { TransactionHistoryQueryDto } from './dto/transactions.dto';

@ApiTags('transactions')
@Controller('transactions')
@UseGuards(JwtAuthGuard, RolesGuard)
export class TransactionsController {
constructor(private readonly transactionsService: TransactionsService) {}

@UseGuards(JwtAuthGuard)
@Get('search')
@ApiOperation({ summary: 'Search transactions with filters and pagination' })
@ApiResponse({ status: 200, description: 'Transaction search results returned successfully' })
search(@Query() query: TransactionSearchQueryDto, @CurrentUser() user: AuthUserPayload) {
return this.transactionsService.search(query, user);
@Get('me')
getMyTransactions(
@CurrentUser() user: AuthUserPayload,
@Query() query: TransactionHistoryQueryDto,
) {
return this.transactionsService.getTransactions(query, user.sub);
}

@UseGuards(JwtAuthGuard)
@Post()
create(@Body() createTransactionDto: CreateTransactionDto, @CurrentUser() user: AuthUserPayload) {
return this.transactionsService.createTransaction(createTransactionDto, user);
@Get('property/:propertyId')
getPropertyTransactions(
@Param('propertyId') propertyId: string,
@Query() query: TransactionHistoryQueryDto,
) {
// Note: In a real scenario, we might want to check if the user has access to this property's history
// For now, we allow authenticated users to see property history as requested.
const propertyQuery = { ...query, propertyId };
return this.transactionsService.getTransactions(propertyQuery);
}

@UseGuards(JwtAuthGuard)
@Get(':id/tax-strategies')
listTaxStrategies(@Param('id') transactionId: string, @CurrentUser() user: AuthUserPayload) {
return this.transactionsService.listTaxStrategies(transactionId, user);
@Roles(UserRole.ADMIN)
@Get()
getAllTransactions(@Query() query: TransactionHistoryQueryDto) {
return this.transactionsService.getTransactions(query);
}

@UseGuards(JwtAuthGuard)
@Post(':id/tax-strategies')
createTaxStrategySuggestion(
@Param('id') transactionId: string,
@Body() createTaxStrategyDto: CreateTransactionTaxStrategyDto,
@Get(':id')
async getTransactionDetails(
@Param('id') id: string,
@CurrentUser() user: AuthUserPayload,
) {
return this.transactionsService.createTaxStrategySuggestion(
transactionId,
createTaxStrategyDto,
user,
);
}
const isAdmin = user.role === UserRole.ADMIN;
const transaction = await this.transactionsService.getTransactionById(id, user.sub, isAdmin);

@UseGuards(JwtAuthGuard)
@Patch(':id/tax-strategies/:strategyId')
updateTaxStrategySuggestion(
@Param('id') transactionId: string,
@Param('strategyId') strategyId: string,
@Body() updateTaxStrategyDto: UpdateTransactionTaxStrategyDto,
@CurrentUser() user: AuthUserPayload,
) {
return this.transactionsService.updateTaxStrategySuggestion(
transactionId,
strategyId,
updateTaxStrategyDto,
user,
);
if (!transaction) {
throw new NotFoundException('Transaction not found or access denied');
}

return transaction;
}
}
15 changes: 5 additions & 10 deletions src/transactions/transactions.module.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from '../database/prisma.module';
import { NotificationsModule } from '../notifications/notifications.module';
import { TransactionsController } from './transactions.controller';
import { TransactionsService } from './transactions.service';
import { DisputesService } from './disputes.service';
import { TimelineService } from './timeline.service';
import { DisputesController } from './disputes.controller';
import { TimelineController } from './timeline.controller';
import { TransactionsController } from './transactions.controller';
import { PrismaModule } from '../database/prisma.module';

@Module({
imports: [PrismaModule, NotificationsModule],
controllers: [TransactionsController, DisputesController, TimelineController],
providers: [TransactionsService, DisputesService, TimelineService],
imports: [PrismaModule],
providers: [TransactionsService],
controllers: [TransactionsController],
exports: [TransactionsService],
})
export class TransactionsModule {}
Loading
Loading