diff --git a/packages/server-nest/src/modules/App/App.module.ts b/packages/server-nest/src/modules/App/App.module.ts index 91ddd24f5..9da8084e5 100644 --- a/packages/server-nest/src/modules/App/App.module.ts +++ b/packages/server-nest/src/modules/App/App.module.ts @@ -78,6 +78,7 @@ import { RolesModule } from '../Roles/Roles.module'; import { SubscriptionModule } from '../Subscription/Subscription.module'; import { OrganizationModule } from '../Organization/Organization.module'; import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module'; +import { PaymentServicesModule } from '../PaymentServices/PaymentServices.module'; @Module({ imports: [ @@ -191,6 +192,7 @@ import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module SubscriptionModule, OrganizationModule, TenantDBManagerModule, + PaymentServicesModule, ], controllers: [AppController], providers: [ diff --git a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts index ac92d49b1..7dfbf4555 100644 --- a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts +++ b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts @@ -1,4 +1,5 @@ import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; +import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, @@ -28,47 +29,77 @@ class AttachmentDto { export class CommandCreditNoteDto { @IsInt() + @ApiProperty({ example: 1, description: 'The customer ID' }) customerId: number; @IsOptional() @IsPositive() + @ApiProperty({ example: 3.43, description: 'The exchange rate' }) exchangeRate?: number; @IsDate() @Type(() => Date) + @ApiProperty({ example: '2021-09-01', description: 'The credit note date' }) creditNoteDate: Date; @IsOptional() @IsString() + @ApiProperty({ example: '123', description: 'The reference number' }) referenceNo?: string; @IsOptional() @IsString() + @ApiProperty({ example: '123', description: 'The credit note number' }) creditNoteNumber?: string; @IsOptional() @IsString() + @ApiProperty({ example: '123', description: 'The note' }) note?: string; @IsOptional() @IsString() + @ApiProperty({ example: '123', description: 'The terms and conditions' }) termsConditions?: string; @IsBoolean() + @ApiProperty({ + example: false, + description: 'The credit note is open', + }) open: boolean = false; @IsOptional() @IsInt() + @ApiProperty({ + example: 1, + description: 'The warehouse ID', + }) warehouseId?: number; @IsOptional() @IsInt() + @ApiProperty({ + example: 1, + description: 'The branch ID', + }) branchId?: number; @IsArray() @ValidateNested({ each: true }) @Type(() => CreditNoteEntryDto) @Min(1) + @ApiProperty({ + example: [ + { + itemId: 1, + quantity: 1, + rate: 10, + taxRateId: 1, + }, + ], + description: 'The credit note entries', + }) entries: CreditNoteEntryDto[]; @IsOptional() @@ -79,14 +110,27 @@ export class CommandCreditNoteDto { @IsOptional() @IsInt() + @ApiProperty({ + example: 1, + description: 'The pdf template ID', + }) pdfTemplateId?: number; @IsOptional() @IsNumber() + @ApiProperty({ + example: 10, + description: 'The discount amount', + }) discount?: number; @IsOptional() @IsEnum(DiscountType) + @ApiProperty({ + example: 'percentage', + description: 'The discount type', + enum: DiscountType, + }) discountType?: DiscountType; @IsOptional() diff --git a/packages/server-nest/src/modules/ItemCategories/dtos/ItemCategory.dto.ts b/packages/server-nest/src/modules/ItemCategories/dtos/ItemCategory.dto.ts index 66b0ddac4..f3b87afe9 100644 --- a/packages/server-nest/src/modules/ItemCategories/dtos/ItemCategory.dto.ts +++ b/packages/server-nest/src/modules/ItemCategories/dtos/ItemCategory.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsNumber } from 'class-validator'; import { IsOptional, IsString } from 'class-validator'; import { IsNotEmpty } from 'class-validator'; @@ -5,30 +6,43 @@ import { IsNotEmpty } from 'class-validator'; class CommandItemCategoryDto { @IsString() @IsNotEmpty() + @ApiProperty({ example: 'Category name', description: 'The category name' }) name: string; @IsString() @IsOptional() + @ApiProperty({ + example: 'Category description', + description: 'The category description', + }) description?: string; @IsNumber() @IsNotEmpty() + @ApiProperty({ + example: 1, + description: 'The user ID', + }) userId: number; @IsNumber() @IsOptional() + @ApiProperty({ example: 1, description: 'The cost account ID' }) costAccountId?: number; @IsNumber() @IsOptional() + @ApiProperty({ example: 1, description: 'The sell account ID' }) sellAccountId?: number; @IsNumber() @IsOptional() + @ApiProperty({ example: 1, description: 'The inventory account ID' }) inventoryAccountId?: number; @IsString() @IsOptional() + @ApiProperty({ example: 'FIFO', description: 'The cost method' }) costMethod?: string; } diff --git a/packages/server-nest/src/modules/PaymentServices/PaymentServices.controller.ts b/packages/server-nest/src/modules/PaymentServices/PaymentServices.controller.ts new file mode 100644 index 000000000..cc1bccd91 --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/PaymentServices.controller.ts @@ -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.', + }); + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/PaymentServices.module.ts b/packages/server-nest/src/modules/PaymentServices/PaymentServices.module.ts index ce9ac93eb..9ed4d17bf 100644 --- a/packages/server-nest/src/modules/PaymentServices/PaymentServices.module.ts +++ b/packages/server-nest/src/modules/PaymentServices/PaymentServices.module.ts @@ -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'; - - - - - -export class PaymentServicesModule {} \ No newline at end of file +@Module({ + providers: [ + DeletePaymentMethodService, + EditPaymentMethodService, + GetPaymentMethodService, + GetPaymentMethodsStateService, + GetPaymentServicesSpecificInvoice, + PaymentServicesApplication, + ], + controllers: [PaymentServicesController], +}) +export class PaymentServicesModule {} diff --git a/packages/server-nest/src/modules/PaymentServices/PaymentServicesApplication.ts b/packages/server-nest/src/modules/PaymentServices/PaymentServicesApplication.ts new file mode 100644 index 000000000..0205f7950 --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/PaymentServicesApplication.ts @@ -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} The payment services for the specified invoice. + */ + public async getPaymentServicesForInvoice(): Promise { + 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} + */ + public async deletePaymentMethod( + paymentIntegrationId: number, + ): Promise { + return this.deletePaymentMethodService.deletePaymentMethod( + paymentIntegrationId, + ); + } + + /** + * Edits the given payment method. + * @param {number} paymentIntegrationId + * @param {EditPaymentMethodDTO} editPaymentMethodDTO + * @returns {Promise} + */ + public async editPaymentMethod( + paymentIntegrationId: number, + editPaymentMethodDTO: EditPaymentMethodDTO, + ): Promise { + return this.editPaymentMethodService.editPaymentMethod( + paymentIntegrationId, + editPaymentMethodDTO, + ); + } + + /** + * Retrieves the payment state providing state. + * @param {number} tenantId + * @returns {Promise} + */ + public async getPaymentMethodsState(): Promise { + return this.getPaymentMethodsStateService.getPaymentMethodsState(); + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/commands/DeletePaymentMethodService.ts b/packages/server-nest/src/modules/PaymentServices/commands/DeletePaymentMethodService.ts new file mode 100644 index 000000000..6cc8a0d72 --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/commands/DeletePaymentMethodService.ts @@ -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} + */ + public async deletePaymentMethod( + paymentIntegrationId: number, + ): Promise { + 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, + }); + }); + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/commands/EditPaymentMethodService.ts b/packages/server-nest/src/modules/PaymentServices/commands/EditPaymentMethodService.ts new file mode 100644 index 000000000..1bc6f957a --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/commands/EditPaymentMethodService.ts @@ -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} + */ + async editPaymentMethod( + paymentIntegrationId: number, + editPaymentMethodDTO: EditPaymentMethodDTO, + ): Promise { + 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, + }); + }); + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentMethodsState.ts b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentMethodsState.ts new file mode 100644 index 000000000..70a184ebb --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentMethodsState.ts @@ -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} + */ + public async getPaymentMethodsState( + ): Promise { + 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; + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentService.ts b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentService.ts new file mode 100644 index 000000000..7db731add --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentService.ts @@ -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} + */ + public async getPaymentMethod( + paymentServiceId: number, + ): Promise { + const stripePayment = await this.paymentIntegrationModel() + .query() + .findById(paymentServiceId) + .throwIfNotFound(); + + return stripePayment; + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoice.ts b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoice.ts new file mode 100644 index 000000000..eaab16921 --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoice.ts @@ -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() + ); + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoiceTransformer.ts b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoiceTransformer.ts new file mode 100644 index 000000000..d82a0db8b --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/queries/GetPaymentServicesSpecificInvoiceTransformer.ts @@ -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'; + } +} diff --git a/packages/server-nest/src/modules/PaymentServices/types.ts b/packages/server-nest/src/modules/PaymentServices/types.ts new file mode 100644 index 000000000..f3ec7b41f --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/types.ts @@ -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; + stripeRedirectUrl: string | null; + }; +} diff --git a/packages/server-nest/src/modules/PaymentServices/utils.ts b/packages/server-nest/src/modules/PaymentServices/utils.ts new file mode 100644 index 000000000..c8ddbc844 --- /dev/null +++ b/packages/server-nest/src/modules/PaymentServices/utils.ts @@ -0,0 +1,9 @@ +import config from '@/config'; + +export const isStripePaymentConfigured = () => { + return ( + config.stripePayment.secretKey && + config.stripePayment.publishableKey && + config.stripePayment.webhooksSecret + ); +}; diff --git a/packages/server-nest/src/modules/Roles/dtos/Role.dto.ts b/packages/server-nest/src/modules/Roles/dtos/Role.dto.ts index 9d8f73238..a2a1090bf 100644 --- a/packages/server-nest/src/modules/Roles/dtos/Role.dto.ts +++ b/packages/server-nest/src/modules/Roles/dtos/Role.dto.ts @@ -1,11 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsNumber, IsString, + Length, MinLength, ValidateNested, } from 'class-validator'; @@ -34,7 +36,10 @@ export class CommandRolePermissionDto { description: 'The value of the permission', }) value: boolean; +} +export class CreateRolePermissionDto extends CommandRolePermissionDto {} +export class EditRolePermissionDto extends CommandRolePermissionDto { @IsNumber() @IsNotEmpty() @ApiProperty({ @@ -60,17 +65,28 @@ class CommandRoleDto { description: 'The description of the role', }) roleDescription: string; +} +export class CreateRoleDto extends CommandRoleDto { @IsArray() + @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => CommandRolePermissionDto) - @MinLength(1) @ApiProperty({ type: [CommandRolePermissionDto], description: 'The permissions of the role', }) - permissions: Array; + permissions: Array; } -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; +}