mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
refactor: payment services to nestjs
This commit is contained in:
@@ -78,6 +78,7 @@ import { RolesModule } from '../Roles/Roles.module';
|
|||||||
import { SubscriptionModule } from '../Subscription/Subscription.module';
|
import { SubscriptionModule } from '../Subscription/Subscription.module';
|
||||||
import { OrganizationModule } from '../Organization/Organization.module';
|
import { OrganizationModule } from '../Organization/Organization.module';
|
||||||
import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module';
|
import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module';
|
||||||
|
import { PaymentServicesModule } from '../PaymentServices/PaymentServices.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -191,6 +192,7 @@ import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module
|
|||||||
SubscriptionModule,
|
SubscriptionModule,
|
||||||
OrganizationModule,
|
OrganizationModule,
|
||||||
TenantDBManagerModule,
|
TenantDBManagerModule,
|
||||||
|
PaymentServicesModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
@@ -28,47 +29,77 @@ class AttachmentDto {
|
|||||||
|
|
||||||
export class CommandCreditNoteDto {
|
export class CommandCreditNoteDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@ApiProperty({ example: 1, description: 'The customer ID' })
|
||||||
customerId: number;
|
customerId: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsPositive()
|
@IsPositive()
|
||||||
|
@ApiProperty({ example: 3.43, description: 'The exchange rate' })
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
|
||||||
@IsDate()
|
@IsDate()
|
||||||
@Type(() => Date)
|
@Type(() => Date)
|
||||||
|
@ApiProperty({ example: '2021-09-01', description: 'The credit note date' })
|
||||||
creditNoteDate: Date;
|
creditNoteDate: Date;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty({ example: '123', description: 'The reference number' })
|
||||||
referenceNo?: string;
|
referenceNo?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty({ example: '123', description: 'The credit note number' })
|
||||||
creditNoteNumber?: string;
|
creditNoteNumber?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty({ example: '123', description: 'The note' })
|
||||||
note?: string;
|
note?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty({ example: '123', description: 'The terms and conditions' })
|
||||||
termsConditions?: string;
|
termsConditions?: string;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
@ApiProperty({
|
||||||
|
example: false,
|
||||||
|
description: 'The credit note is open',
|
||||||
|
})
|
||||||
open: boolean = false;
|
open: boolean = false;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The warehouse ID',
|
||||||
|
})
|
||||||
warehouseId?: number;
|
warehouseId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The branch ID',
|
||||||
|
})
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreditNoteEntryDto)
|
@Type(() => CreditNoteEntryDto)
|
||||||
@Min(1)
|
@Min(1)
|
||||||
|
@ApiProperty({
|
||||||
|
example: [
|
||||||
|
{
|
||||||
|
itemId: 1,
|
||||||
|
quantity: 1,
|
||||||
|
rate: 10,
|
||||||
|
taxRateId: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'The credit note entries',
|
||||||
|
})
|
||||||
entries: CreditNoteEntryDto[];
|
entries: CreditNoteEntryDto[];
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -79,14 +110,27 @@ export class CommandCreditNoteDto {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The pdf template ID',
|
||||||
|
})
|
||||||
pdfTemplateId?: number;
|
pdfTemplateId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 10,
|
||||||
|
description: 'The discount amount',
|
||||||
|
})
|
||||||
discount?: number;
|
discount?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(DiscountType)
|
@IsEnum(DiscountType)
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'percentage',
|
||||||
|
description: 'The discount type',
|
||||||
|
enum: DiscountType,
|
||||||
|
})
|
||||||
discountType?: DiscountType;
|
discountType?: DiscountType;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsNumber } from 'class-validator';
|
import { IsNumber } from 'class-validator';
|
||||||
import { IsOptional, IsString } from 'class-validator';
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
import { IsNotEmpty } from 'class-validator';
|
import { IsNotEmpty } from 'class-validator';
|
||||||
@@ -5,30 +6,43 @@ import { IsNotEmpty } from 'class-validator';
|
|||||||
class CommandItemCategoryDto {
|
class CommandItemCategoryDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ example: 'Category name', description: 'The category name' })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'Category description',
|
||||||
|
description: 'The category description',
|
||||||
|
})
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The user ID',
|
||||||
|
})
|
||||||
userId: number;
|
userId: number;
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiProperty({ example: 1, description: 'The cost account ID' })
|
||||||
costAccountId?: number;
|
costAccountId?: number;
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiProperty({ example: 1, description: 'The sell account ID' })
|
||||||
sellAccountId?: number;
|
sellAccountId?: number;
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiProperty({ example: 1, description: 'The inventory account ID' })
|
||||||
inventoryAccountId?: number;
|
inventoryAccountId?: number;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiProperty({ example: 'FIFO', description: 'The cost method' })
|
||||||
costMethod?: string;
|
costMethod?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Delete,
|
||||||
|
Param,
|
||||||
|
Body,
|
||||||
|
Req,
|
||||||
|
Res,
|
||||||
|
Next,
|
||||||
|
UsePipes,
|
||||||
|
ValidationPipe,
|
||||||
|
HttpStatus,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { PaymentServicesApplication } from './PaymentServicesApplication';
|
||||||
|
|
||||||
|
@ApiTags('PaymentServices')
|
||||||
|
@Controller('payment-services')
|
||||||
|
export class PaymentServicesController {
|
||||||
|
constructor(
|
||||||
|
private readonly paymentServicesApp: PaymentServicesApplication,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('/')
|
||||||
|
async getPaymentServicesSpecificInvoice(@Res() res: Response) {
|
||||||
|
const paymentServices =
|
||||||
|
await this.paymentServicesApp.getPaymentServicesForInvoice();
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).send({ paymentServices });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/state')
|
||||||
|
async getPaymentMethodsState(@Res() res: Response) {
|
||||||
|
const paymentMethodsState =
|
||||||
|
await this.paymentServicesApp.getPaymentMethodsState();
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).send({ data: paymentMethodsState });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/:paymentServiceId')
|
||||||
|
async getPaymentService(
|
||||||
|
@Param('paymentServiceId') paymentServiceId: number,
|
||||||
|
@Req() req: Request,
|
||||||
|
@Res() res: Response,
|
||||||
|
@Next() next: NextFunction,
|
||||||
|
) {
|
||||||
|
const paymentService =
|
||||||
|
await this.paymentServicesApp.getPaymentService(paymentServiceId);
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).send({ data: paymentService });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/:paymentMethodId')
|
||||||
|
@UsePipes(new ValidationPipe({ whitelist: true }))
|
||||||
|
async updatePaymentMethod(
|
||||||
|
@Param('paymentMethodId') paymentMethodId: number,
|
||||||
|
@Body() updatePaymentMethodDTO: any,
|
||||||
|
@Req() req: Request,
|
||||||
|
@Res() res: Response,
|
||||||
|
@Next() next: NextFunction,
|
||||||
|
) {
|
||||||
|
await this.paymentServicesApp.editPaymentMethod(
|
||||||
|
paymentMethodId,
|
||||||
|
updatePaymentMethodDTO,
|
||||||
|
);
|
||||||
|
return res.status(HttpStatus.OK).send({
|
||||||
|
id: paymentMethodId,
|
||||||
|
message: 'The given payment method has been updated.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('/:paymentMethodId')
|
||||||
|
async deletePaymentMethod(
|
||||||
|
@Param('paymentMethodId') paymentMethodId: number,
|
||||||
|
@Res() res: Response,
|
||||||
|
) {
|
||||||
|
await this.paymentServicesApp.deletePaymentMethod(paymentMethodId);
|
||||||
|
|
||||||
|
return res.status(HttpStatus.NO_CONTENT).send({
|
||||||
|
id: paymentMethodId,
|
||||||
|
message: 'The payment method has been deleted.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,21 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { DeletePaymentMethodService } from './commands/DeletePaymentMethodService';
|
||||||
|
import { EditPaymentMethodService } from './commands/EditPaymentMethodService';
|
||||||
|
import { GetPaymentMethodService } from './queries/GetPaymentService';
|
||||||
|
import { GetPaymentServicesSpecificInvoice } from './queries/GetPaymentServicesSpecificInvoice';
|
||||||
|
import { GetPaymentMethodsStateService } from './queries/GetPaymentMethodsState';
|
||||||
|
import { PaymentServicesApplication } from './PaymentServicesApplication';
|
||||||
|
import { PaymentServicesController } from './PaymentServices.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [
|
||||||
|
DeletePaymentMethodService,
|
||||||
|
EditPaymentMethodService,
|
||||||
|
GetPaymentMethodService,
|
||||||
|
GetPaymentMethodsStateService,
|
||||||
|
GetPaymentServicesSpecificInvoice,
|
||||||
|
PaymentServicesApplication,
|
||||||
|
],
|
||||||
|
controllers: [PaymentServicesController],
|
||||||
|
})
|
||||||
export class PaymentServicesModule {}
|
export class PaymentServicesModule {}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { GetPaymentServicesSpecificInvoice } from './queries/GetPaymentServicesSpecificInvoice';
|
||||||
|
import { DeletePaymentMethodService } from './commands/DeletePaymentMethodService';
|
||||||
|
import { EditPaymentMethodService } from './commands/EditPaymentMethodService';
|
||||||
|
import { EditPaymentMethodDTO, GetPaymentMethodsPOJO } from './types';
|
||||||
|
import { GetPaymentMethodsStateService } from './queries/GetPaymentMethodsState';
|
||||||
|
import { GetPaymentMethodService } from './queries/GetPaymentService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class PaymentServicesApplication {
|
||||||
|
constructor(
|
||||||
|
private readonly getPaymentServicesSpecificInvoice: GetPaymentServicesSpecificInvoice,
|
||||||
|
private readonly deletePaymentMethodService: DeletePaymentMethodService,
|
||||||
|
private readonly editPaymentMethodService: EditPaymentMethodService,
|
||||||
|
private readonly getPaymentMethodsStateService: GetPaymentMethodsStateService,
|
||||||
|
private readonly getPaymentMethodService: GetPaymentMethodService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment services for a specific invoice.
|
||||||
|
* @param {number} invoiceId - The ID of the invoice.
|
||||||
|
* @returns {Promise<any>} The payment services for the specified invoice.
|
||||||
|
*/
|
||||||
|
public async getPaymentServicesForInvoice(): Promise<any> {
|
||||||
|
return this.getPaymentServicesSpecificInvoice.getPaymentServicesInvoice(
|
||||||
|
tenantId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves specific payment service details.
|
||||||
|
* @param {number} paymentServiceId - Payment service id.
|
||||||
|
*/
|
||||||
|
public async getPaymentService(paymentServiceId: number) {
|
||||||
|
return this.getPaymentMethodService.getPaymentMethod(paymentServiceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given payment method.
|
||||||
|
* @param {number} paymentIntegrationId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async deletePaymentMethod(
|
||||||
|
paymentIntegrationId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
return this.deletePaymentMethodService.deletePaymentMethod(
|
||||||
|
paymentIntegrationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given payment method.
|
||||||
|
* @param {number} paymentIntegrationId
|
||||||
|
* @param {EditPaymentMethodDTO} editPaymentMethodDTO
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async editPaymentMethod(
|
||||||
|
paymentIntegrationId: number,
|
||||||
|
editPaymentMethodDTO: EditPaymentMethodDTO,
|
||||||
|
): Promise<void> {
|
||||||
|
return this.editPaymentMethodService.editPaymentMethod(
|
||||||
|
paymentIntegrationId,
|
||||||
|
editPaymentMethodDTO,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment state providing state.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @returns {Promise<GetPaymentMethodsPOJO>}
|
||||||
|
*/
|
||||||
|
public async getPaymentMethodsState(): Promise<GetPaymentMethodsPOJO> {
|
||||||
|
return this.getPaymentMethodsStateService.getPaymentMethodsState();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||||
|
import { TransactionPaymentServiceEntry } from '../models/TransactionPaymentServiceEntry.model';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeletePaymentMethodService {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(PaymentIntegration.name)
|
||||||
|
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||||
|
typeof PaymentIntegration
|
||||||
|
>,
|
||||||
|
|
||||||
|
@Inject(TransactionPaymentServiceEntry.name)
|
||||||
|
private readonly transactionPaymentServiceEntryModel: TenantModelProxy<
|
||||||
|
typeof TransactionPaymentServiceEntry
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given payment integration.
|
||||||
|
* @param {number} paymentIntegrationId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async deletePaymentMethod(
|
||||||
|
paymentIntegrationId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const paymentIntegration = await this.paymentIntegrationModel()
|
||||||
|
.query()
|
||||||
|
.findById(paymentIntegrationId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Delete payment methods links.
|
||||||
|
await this.transactionPaymentServiceEntryModel()
|
||||||
|
.query(trx)
|
||||||
|
.where('paymentIntegrationId', paymentIntegrationId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Delete the payment integration.
|
||||||
|
await this.paymentIntegrationModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(paymentIntegrationId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onPaymentMethodDeleted` event.
|
||||||
|
await this.eventEmitter.emitAsync(events.paymentMethod.onDeleted, {
|
||||||
|
paymentIntegrationId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { EditPaymentMethodDTO } from '../types';
|
||||||
|
import { TransactionPaymentServiceEntry } from '../models/TransactionPaymentServiceEntry.model';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EditPaymentMethodService {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(PaymentIntegration.name)
|
||||||
|
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||||
|
typeof PaymentIntegration
|
||||||
|
>,
|
||||||
|
|
||||||
|
@Inject(TransactionPaymentServiceEntry.name)
|
||||||
|
private readonly transactionPaymentServiceEntryModel: TenantModelProxy<
|
||||||
|
typeof TransactionPaymentServiceEntry
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given payment method.
|
||||||
|
* @param {number} paymentIntegrationId - The ID of the payment method.
|
||||||
|
* @param {EditPaymentMethodDTO} editPaymentMethodDTO
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async editPaymentMethod(
|
||||||
|
paymentIntegrationId: number,
|
||||||
|
editPaymentMethodDTO: EditPaymentMethodDTO,
|
||||||
|
): Promise<void> {
|
||||||
|
const paymentMethod = await this.paymentIntegrationModel()
|
||||||
|
.query()
|
||||||
|
.findById(paymentIntegrationId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onPaymentMethodEditing` event.
|
||||||
|
await this.eventEmitter.emitAsync(events.paymentMethod.onEditing, {
|
||||||
|
paymentIntegrationId,
|
||||||
|
editPaymentMethodDTO,
|
||||||
|
trx,
|
||||||
|
});
|
||||||
|
await this.paymentIntegrationModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(paymentIntegrationId)
|
||||||
|
.patch({
|
||||||
|
...editPaymentMethodDTO,
|
||||||
|
});
|
||||||
|
// Triggers `onPaymentMethodEdited` event.
|
||||||
|
await this.eventEmitter.emitAsync(events.paymentMethod.onEdited, {
|
||||||
|
paymentIntegrationId,
|
||||||
|
editPaymentMethodDTO,
|
||||||
|
trx,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { GetPaymentMethodsPOJO } from '../types';
|
||||||
|
import { isStripePaymentConfigured } from '../utils';
|
||||||
|
import { GetStripeAuthorizationLinkService } from '../../StripePayment/GetStripeAuthorizationLink';
|
||||||
|
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetPaymentMethodsStateService {
|
||||||
|
constructor(
|
||||||
|
private readonly getStripeAuthorizationLinkService: GetStripeAuthorizationLinkService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
|
||||||
|
@Inject(PaymentIntegration.name)
|
||||||
|
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||||
|
typeof PaymentIntegration
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment state provising state.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @returns {Promise<GetPaymentMethodsPOJO>}
|
||||||
|
*/
|
||||||
|
public async getPaymentMethodsState(
|
||||||
|
): Promise<GetPaymentMethodsPOJO> {
|
||||||
|
const stripePayment = await this.paymentIntegrationModel()
|
||||||
|
.query()
|
||||||
|
.orderBy('createdAt', 'ASC')
|
||||||
|
.findOne({
|
||||||
|
service: 'Stripe',
|
||||||
|
});
|
||||||
|
const isStripeAccountCreated = !!stripePayment;
|
||||||
|
const isStripePaymentEnabled = stripePayment?.paymentEnabled;
|
||||||
|
const isStripePayoutEnabled = stripePayment?.payoutEnabled;
|
||||||
|
const isStripeEnabled = stripePayment?.fullEnabled;
|
||||||
|
|
||||||
|
const stripePaymentMethodId = stripePayment?.id || null;
|
||||||
|
const stripeAccountId = stripePayment?.accountId || null;
|
||||||
|
const stripePublishableKey = this.configService.get(
|
||||||
|
'stripePayment.publishableKey',
|
||||||
|
);
|
||||||
|
const stripeCurrencies = ['USD', 'EUR'];
|
||||||
|
const stripeRedirectUrl = 'https://your-stripe-redirect-url.com';
|
||||||
|
const isStripeServerConfigured = isStripePaymentConfigured();
|
||||||
|
const stripeAuthLink =
|
||||||
|
this.getStripeAuthorizationLinkService.getStripeAuthLink();
|
||||||
|
|
||||||
|
const paymentMethodPOJO: GetPaymentMethodsPOJO = {
|
||||||
|
stripe: {
|
||||||
|
isStripeAccountCreated,
|
||||||
|
isStripePaymentEnabled,
|
||||||
|
isStripePayoutEnabled,
|
||||||
|
isStripeEnabled,
|
||||||
|
isStripeServerConfigured,
|
||||||
|
stripeAccountId,
|
||||||
|
stripePaymentMethodId,
|
||||||
|
stripePublishableKey,
|
||||||
|
stripeCurrencies,
|
||||||
|
stripeAuthLink,
|
||||||
|
stripeRedirectUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return paymentMethodPOJO;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { GetPaymentMethodsPOJO } from '../types';
|
||||||
|
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetPaymentMethodService {
|
||||||
|
constructor(
|
||||||
|
@Inject(PaymentIntegration.name)
|
||||||
|
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||||
|
typeof PaymentIntegration
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment state provising state.
|
||||||
|
* @returns {Promise<GetPaymentMethodsPOJO>}
|
||||||
|
*/
|
||||||
|
public async getPaymentMethod(
|
||||||
|
paymentServiceId: number,
|
||||||
|
): Promise<GetPaymentMethodsPOJO> {
|
||||||
|
const stripePayment = await this.paymentIntegrationModel()
|
||||||
|
.query()
|
||||||
|
.findById(paymentServiceId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
return stripePayment;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
import { GetPaymentServicesSpecificInvoiceTransformer } from './GetPaymentServicesSpecificInvoiceTransformer';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class GetPaymentServicesSpecificInvoice {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private transform: TransformerInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment services of the given invoice.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} invoiceId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getPaymentServicesInvoice(tenantId: number) {
|
||||||
|
const { PaymentIntegration } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const paymentGateways = await PaymentIntegration.query()
|
||||||
|
.modify('fullEnabled')
|
||||||
|
.orderBy('name', 'ASC');
|
||||||
|
|
||||||
|
return this.transform.transform(
|
||||||
|
tenantId,
|
||||||
|
paymentGateways,
|
||||||
|
new GetPaymentServicesSpecificInvoiceTransformer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||||
|
|
||||||
|
export class GetPaymentServicesSpecificInvoiceTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Exclude attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['accountId'];
|
||||||
|
};
|
||||||
|
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return ['serviceFormatted'];
|
||||||
|
};
|
||||||
|
|
||||||
|
public serviceFormatted(method) {
|
||||||
|
return 'Stripe';
|
||||||
|
}
|
||||||
|
}
|
||||||
33
packages/server-nest/src/modules/PaymentServices/types.ts
Normal file
33
packages/server-nest/src/modules/PaymentServices/types.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export interface EditPaymentMethodDTO {
|
||||||
|
name?: string;
|
||||||
|
options?: {
|
||||||
|
bankAccountId?: number; // bank account.
|
||||||
|
clearningAccountId?: number; // current liability.
|
||||||
|
|
||||||
|
showVisa?: boolean;
|
||||||
|
showMasterCard?: boolean;
|
||||||
|
showDiscover?: boolean;
|
||||||
|
showAmer?: boolean;
|
||||||
|
showJcb?: boolean;
|
||||||
|
showDiners?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetPaymentMethodsPOJO {
|
||||||
|
stripe: {
|
||||||
|
isStripeAccountCreated: boolean;
|
||||||
|
|
||||||
|
isStripePaymentEnabled: boolean;
|
||||||
|
isStripePayoutEnabled: boolean;
|
||||||
|
isStripeEnabled: boolean;
|
||||||
|
|
||||||
|
isStripeServerConfigured: boolean;
|
||||||
|
|
||||||
|
stripeAccountId: string | null;
|
||||||
|
stripePaymentMethodId: number | null;
|
||||||
|
stripePublishableKey: string | null;
|
||||||
|
stripeAuthLink: string;
|
||||||
|
stripeCurrencies: Array<string>;
|
||||||
|
stripeRedirectUrl: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import config from '@/config';
|
||||||
|
|
||||||
|
export const isStripePaymentConfigured = () => {
|
||||||
|
return (
|
||||||
|
config.stripePayment.secretKey &&
|
||||||
|
config.stripePayment.publishableKey &&
|
||||||
|
config.stripePayment.webhooksSecret
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
|
ArrayMinSize,
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsString,
|
IsString,
|
||||||
|
Length,
|
||||||
MinLength,
|
MinLength,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
@@ -34,7 +36,10 @@ export class CommandRolePermissionDto {
|
|||||||
description: 'The value of the permission',
|
description: 'The value of the permission',
|
||||||
})
|
})
|
||||||
value: boolean;
|
value: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateRolePermissionDto extends CommandRolePermissionDto {}
|
||||||
|
export class EditRolePermissionDto extends CommandRolePermissionDto {
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -60,17 +65,28 @@ class CommandRoleDto {
|
|||||||
description: 'The description of the role',
|
description: 'The description of the role',
|
||||||
})
|
})
|
||||||
roleDescription: string;
|
roleDescription: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateRoleDto extends CommandRoleDto {
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
@ArrayMinSize(1)
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CommandRolePermissionDto)
|
@Type(() => CommandRolePermissionDto)
|
||||||
@MinLength(1)
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: [CommandRolePermissionDto],
|
type: [CommandRolePermissionDto],
|
||||||
description: 'The permissions of the role',
|
description: 'The permissions of the role',
|
||||||
})
|
})
|
||||||
permissions: Array<CommandRolePermissionDto>;
|
permissions: Array<CreateRolePermissionDto>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateRoleDto extends CommandRoleDto {}
|
export class EditRoleDto extends CommandRoleDto {
|
||||||
export class EditRoleDto extends CommandRoleDto {}
|
@IsArray()
|
||||||
|
@ArrayMinSize(1)
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => CommandRolePermissionDto)
|
||||||
|
@ApiProperty({
|
||||||
|
type: [CommandRolePermissionDto],
|
||||||
|
description: 'The permissions of the role',
|
||||||
|
})
|
||||||
|
permissions: Array<EditRolePermissionDto>;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user