refactor: the invoice service

This commit is contained in:
mahyargdz
2025-04-19 16:28:39 +03:30
parent 1567d61aca
commit db9fff066b
2 changed files with 84 additions and 45 deletions
@@ -114,7 +114,6 @@ export class InvoicesService {
//********************************** */
//TODO: fix this
async updateInvoiceAdmin(invoiceId: string, updateDto: UpdateInvoiceDto) {
const queryRunner = this.dataSource.createQueryRunner();
@@ -158,6 +157,16 @@ export class InvoicesService {
const user = await this.usersService.findOneByIdWithQueryRunner(updateDto.userId, queryRunner);
invoice.user = user;
}
// Save the updated invoice
await queryRunner.manager.save(this.invoiceRepository.target, invoice);
await queryRunner.commitTransaction();
return {
message: InvoiceMessage.INVOICE_UPDATED,
invoice,
};
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
@@ -168,39 +177,48 @@ export class InvoicesService {
//********************************** */
async approveInvoiceRequest(invoiceId: string, userId: string) {
const { user } = await this.usersService.findOneById(userId);
const queryRunner = this.dataSource.createQueryRunner();
const invoice = await this.invoiceRepository.findOne({ where: { id: invoiceId, user: { id: userId } }, relations: { items: true } });
if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID);
try {
await queryRunner.connect();
await queryRunner.startTransaction();
if (invoice.status === InvoiceStatus.WAIT_PAYMENT) throw new BadRequestException(InvoiceMessage.ALREADY_APPROVED);
if (invoice.status === InvoiceStatus.PAID) throw new BadRequestException(InvoiceMessage.INVOICE_ALREADY_PAID);
if (dayjs().isAfter(dayjs(invoice.dueDate).add(INVOICE.MAX_DAYS_AFTER_OVERDUE, "day"))) {
invoice.status = InvoiceStatus.EXPIRED;
await this.invoiceRepository.save(invoice);
throw new BadRequestException(InvoiceMessage.INVOICE_IS_OVERDUE);
}
const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner);
if (invoice.status !== InvoiceStatus.PENDING) throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_APPROVED);
const invoice = await this.validateInvoiceForApproval(invoiceId, userId, queryRunner);
const existCode = await this.otpService.checkExistOtp(user.phone, "INVOICE_VERIFY");
if (existCode) {
return {
message: AuthMessage.OTP_ALREADY_SENT,
ttlSecond: existCode,
};
}
// Generate and send OTP
const otpCode = await this.otpService.generateAndSetInCache(user.phone, "INVOICE_VERIFY");
const items = invoice.items.map((item) => item.name).join(", ");
await this.smsService.sendInvoiceVerifyCode(
user.phone,
otpCode,
invoice.numericId,
new Decimal(invoice.totalPrice).toNumber(),
items,
);
await queryRunner.commitTransaction();
const existCode = await this.otpService.checkExistOtp(user.phone, "INVOICE_VERIFY");
if (existCode) {
return {
message: AuthMessage.OTP_ALREADY_SENT,
ttlSecond: existCode,
message: AuthMessage.OTP_SENT,
otpCode,
};
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
const otpCode = await this.otpService.generateAndSetInCache(user.phone, "INVOICE_VERIFY");
const items = invoice.items.map((item) => item.name).join(", ");
//
await this.smsService.sendInvoiceVerifyCode(user.phone, otpCode, invoice.numericId, new Decimal(invoice.totalPrice).toNumber(), items);
this.logger.debug(`OTP sent to ${user.phone}: ${otpCode}`);
return {
message: AuthMessage.OTP_SENT,
otpCode,
};
}
//********************************** */
@@ -213,27 +231,12 @@ export class InvoicesService {
await queryRunner.startTransaction();
const { code } = verifyOtpDto;
const invoice = await queryRunner.manager.findOne(Invoice, {
where: { id: invoiceId, user: { id: userId } },
relations: { items: true },
});
if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID);
const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner);
const isValid = await this.otpService.verifyOtp(user.phone, code, "INVOICE_VERIFY");
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
if (invoice.status === InvoiceStatus.WAIT_PAYMENT) throw new BadRequestException(InvoiceMessage.ALREADY_APPROVED);
if (invoice.status === InvoiceStatus.PAID) throw new BadRequestException(InvoiceMessage.INVOICE_ALREADY_PAID);
//
if (dayjs().isAfter(dayjs(invoice.dueDate).add(INVOICE.MAX_DAYS_AFTER_OVERDUE, "day"))) {
invoice.status = InvoiceStatus.EXPIRED;
await this.invoiceRepository.save(invoice);
throw new BadRequestException(InvoiceMessage.INVOICE_IS_OVERDUE);
}
//
if (invoice.status !== InvoiceStatus.PENDING) throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_APPROVED);
const invoice = await this.validateInvoiceForApproval(invoiceId, userId, queryRunner);
invoice.status = InvoiceStatus.WAIT_PAYMENT;
await queryRunner.manager.save(Invoice, invoice);
@@ -267,7 +270,33 @@ export class InvoicesService {
await queryRunner.release();
}
}
//********************************** */
private async validateInvoiceForApproval(invoiceId: string, userId: string, queryRunner: QueryRunner): Promise<Invoice> {
const invoice = await queryRunner.manager.findOne(Invoice, {
where: { id: invoiceId, user: { id: userId } },
relations: { items: true },
});
if (!invoice) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID);
if (invoice.status === InvoiceStatus.WAIT_PAYMENT) throw new BadRequestException(InvoiceMessage.ALREADY_APPROVED);
if (invoice.status === InvoiceStatus.PAID) throw new BadRequestException(InvoiceMessage.INVOICE_ALREADY_PAID);
if (dayjs().isAfter(dayjs(invoice.dueDate).add(INVOICE.MAX_DAYS_AFTER_OVERDUE, "day"))) {
invoice.status = InvoiceStatus.EXPIRED;
await queryRunner.manager.save(Invoice, invoice);
throw new BadRequestException(InvoiceMessage.INVOICE_IS_OVERDUE);
}
if (invoice.status !== InvoiceStatus.PENDING) throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_APPROVED);
return invoice;
}
///********************************** */
async createInvoiceForSubscription(user: User, plan: SubscriptionPlan, userSub: UserSubscription, dueDate: Date, qryRnr: QueryRunner) {
const discount = plan.directDiscount;
const originalPrice = plan.originalPrice || plan.price;