invoice event
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
export class InvoiceCreatedEvent {
|
||||||
|
constructor(
|
||||||
|
public readonly invoiceId: string,
|
||||||
|
public readonly invoiceNumber: string,
|
||||||
|
public readonly total: number,
|
||||||
|
public readonly userId: string,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export class OrderStatusChangedEvent {
|
||||||
|
// constructor(
|
||||||
|
// public readonly orderId: string,
|
||||||
|
// public readonly userId: string,
|
||||||
|
// public readonly orderNumber: string,
|
||||||
|
// public readonly: string,
|
||||||
|
// public readonly previousStatus: OrderStatusEnum,
|
||||||
|
// public readonly newStatus: OrderStatusEnum,
|
||||||
|
// ) { }
|
||||||
|
// }
|
||||||
@@ -6,9 +6,11 @@ import { Invoice } from './entities/invoice.entity';
|
|||||||
import { InvoiceItem } from './entities/invoice-item.entity';
|
import { InvoiceItem } from './entities/invoice-item.entity';
|
||||||
import { ProductModule } from '../product/product.module';
|
import { ProductModule } from '../product/product.module';
|
||||||
import { InvoiceRepository } from './repositories/invoice.repository';
|
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||||
|
import { InvoiceListeners } from './listeners/invoice.listeners';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { RolesModule } from '../roles/roles.module';
|
import { RolesModule } from '../roles/roles.module';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
import { NotificationsModule } from '../notification/notifications.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -16,10 +18,11 @@ import { AuthModule } from '../auth/auth.module';
|
|||||||
ProductModule,
|
ProductModule,
|
||||||
RolesModule,
|
RolesModule,
|
||||||
JwtModule,
|
JwtModule,
|
||||||
AuthModule
|
AuthModule,
|
||||||
|
NotificationsModule,
|
||||||
],
|
],
|
||||||
controllers: [InvoiceController],
|
controllers: [InvoiceController],
|
||||||
providers: [InvoiceService, InvoiceRepository],
|
providers: [InvoiceService, InvoiceRepository, InvoiceListeners],
|
||||||
exports: [InvoiceService, InvoiceRepository],
|
exports: [InvoiceService, InvoiceRepository],
|
||||||
})
|
})
|
||||||
export class InvoiceModule { }
|
export class InvoiceModule {}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
||||||
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
||||||
import { FindInvoicesDto } from './dto/find-invoices.dto';
|
import { FindInvoicesDto } from './dto/find-invoices.dto';
|
||||||
@@ -9,6 +10,7 @@ import { Request } from '../request/entities/request.entity';
|
|||||||
import { User } from '../user/entities/user.entity';
|
import { User } from '../user/entities/user.entity';
|
||||||
import { ProductService } from '../product/providers/product.service';
|
import { ProductService } from '../product/providers/product.service';
|
||||||
import { InvoiceRepository } from './repositories/invoice.repository';
|
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||||
|
import { InvoiceCreatedEvent } from './events/invoice.events';
|
||||||
|
|
||||||
const TAX_RATE = 0.1;
|
const TAX_RATE = 0.1;
|
||||||
|
|
||||||
@@ -18,7 +20,8 @@ export class InvoiceService {
|
|||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
private readonly productService: ProductService,
|
private readonly productService: ProductService,
|
||||||
private readonly invoiceRepository: InvoiceRepository,
|
private readonly invoiceRepository: InvoiceRepository,
|
||||||
) { }
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
|
||||||
async create(dto: CreateInvoiceDto) {
|
async create(dto: CreateInvoiceDto) {
|
||||||
@@ -49,7 +52,7 @@ export class InvoiceService {
|
|||||||
|
|
||||||
const discount = dto.items.reduce((acc, item) => acc + (item.discount ?? 0), 0);
|
const discount = dto.items.reduce((acc, item) => acc + (item.discount ?? 0), 0);
|
||||||
|
|
||||||
return this.em.transactional(async (em) => {
|
const invoice = await this.em.transactional(async (em) => {
|
||||||
const invoice = em.create(Invoice, {
|
const invoice = em.create(Invoice, {
|
||||||
user,
|
user,
|
||||||
...(request && { request }),
|
...(request && { request }),
|
||||||
@@ -98,6 +101,18 @@ export class InvoiceService {
|
|||||||
await em.flush();
|
await em.flush();
|
||||||
return invoice;
|
return invoice;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.eventEmitter.emit(
|
||||||
|
InvoiceCreatedEvent.name,
|
||||||
|
new InvoiceCreatedEvent(
|
||||||
|
invoice.id,
|
||||||
|
String(invoice.invoiceNumber),
|
||||||
|
invoice.total!,
|
||||||
|
user.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(userId: string, dto: FindInvoicesDto) {
|
findAll(userId: string, dto: FindInvoicesDto) {
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { NotificationQueueService } from 'src/modules/notification/services/notification-queue.service';
|
||||||
|
import { InvoiceCreatedEvent } from '../events/invoice.events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InvoiceListeners {
|
||||||
|
private readonly logger = new Logger(InvoiceListeners.name);
|
||||||
|
private invoiceCreatedSmsTemplateId: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly notificationQueueService: NotificationQueueService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {
|
||||||
|
this.invoiceCreatedSmsTemplateId =
|
||||||
|
this.configService.get<string>('SMS_PATTERN_INVOICE_CREATED') ?? 'invoice-created';
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent(InvoiceCreatedEvent.name)
|
||||||
|
async handleInvoiceCreated(event: InvoiceCreatedEvent) {
|
||||||
|
try {
|
||||||
|
this.logger.log(
|
||||||
|
`Invoice created event received: ${event.invoiceId} for user: ${event.userId}, invoice number: ${event.invoiceNumber}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.notificationQueueService.addSmsNotification({
|
||||||
|
recipient: { userId: event.userId },
|
||||||
|
templateId: this.invoiceCreatedSmsTemplateId,
|
||||||
|
parameters: {
|
||||||
|
invoiceNumber: event.invoiceNumber,
|
||||||
|
total: event.total.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.log(`Invoice created notification queued for user ${event.userId}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(
|
||||||
|
`Failed to send notification for invoice created event: ${event.invoiceId}`,
|
||||||
|
error instanceof Error ? error.stack : String(error),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,6 +64,15 @@ export class LearningController {
|
|||||||
|
|
||||||
// ----------------- Admin routes -----------------
|
// ----------------- Admin routes -----------------
|
||||||
|
|
||||||
|
@Get('admin/learnings')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
||||||
|
@ApiOperation({ summary: 'Find all learnings videos as admin' })
|
||||||
|
findAllLearningsAsAdmin() {
|
||||||
|
return this.learningService.findLearningsVideos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Post('admin/learnings')
|
@Post('admin/learnings')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
||||||
@@ -96,6 +105,14 @@ export class LearningController {
|
|||||||
return this.learningService.createLearningCategory(createDto);
|
return this.learningService.createLearningCategory(createDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('admin/learnings/category')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
||||||
|
@ApiOperation({ summary: 'Get all learning categories' })
|
||||||
|
getCategories() {
|
||||||
|
return this.learningService.findCategories()
|
||||||
|
}
|
||||||
|
|
||||||
@Patch('admin/learnings/category/:id')
|
@Patch('admin/learnings/category/:id')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
@Permissions(PermissionEnum.MANAGE_LEARNINGS)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export class LearningService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findLearningsVideos() {
|
async findLearningsVideos() {
|
||||||
const learnings = await this.learningRepository.findAll();
|
const learnings = await this.learningRepository.findAll({populate: ['category']});
|
||||||
|
|
||||||
return { learnings };
|
return { learnings };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user