diff --git a/services/workflows-service/src/business-report/business-report.controller.external.ts b/services/workflows-service/src/business-report/business-report.controller.external.ts index 9a1d5616a7..6aab84877d 100644 --- a/services/workflows-service/src/business-report/business-report.controller.external.ts +++ b/services/workflows-service/src/business-report/business-report.controller.external.ts @@ -166,9 +166,30 @@ export class BusinessReportControllerExternal { @CurrentProject() currentProjectId: TProjectId, @Query() { from, to }: BusinessReportMetricsRequestQueryDto, ) { - const { id: customerId } = await this.customerService.getByProjectId(currentProjectId); + const { id: customerId, features } = await this.customerService.getByProjectId( + currentProjectId, + ); - return await this.merchantMonitoringClient.getMetrics({ customerId, from, to }); + const { totalActiveMerchants, addedMerchantsCount, unmonitoredMerchants } = + await this.businessService.getMerchantMonitoringMetrics({ + projectIds: [currentProjectId], + features, + from, + to, + }); + + const merchantMonitoringMetrics = await this.merchantMonitoringClient.getMetrics({ + customerId, + from, + to, + }); + + return { + ...merchantMonitoringMetrics, + totalActiveMerchants, + addedMerchantsCount, + removedMerchantsCount: unmonitoredMerchants, + }; } @common.Post() diff --git a/services/workflows-service/src/business/business.controller.external.ts b/services/workflows-service/src/business/business.controller.external.ts index 002e9d8a9b..f3694f3cac 100755 --- a/services/workflows-service/src/business/business.controller.external.ts +++ b/services/workflows-service/src/business/business.controller.external.ts @@ -155,12 +155,14 @@ export class BusinessControllerExternal { featureConfig?: TCustomerWithFeatures['features']; }; + const isEnabled = data.state === 'on'; const updatedMetadata = _.merge({}, metadata, { featureConfig: { [FEATURE_LIST.ONGOING_MERCHANT_REPORT]: { - enabled: data.state === 'on', + enabled: isEnabled, reason: data.reason ?? null, userReason: data.userReason ?? null, + disabledAt: isEnabled ? null : new Date().getTime(), }, }, }); diff --git a/services/workflows-service/src/business/business.repository.ts b/services/workflows-service/src/business/business.repository.ts index 1e00263711..4dc2c65550 100644 --- a/services/workflows-service/src/business/business.repository.ts +++ b/services/workflows-service/src/business/business.repository.ts @@ -106,4 +106,13 @@ export class BusinessRepository { ...args, }); } + + async count( + args: Prisma.SelectSubset, + projectIds: TProjectIds, + ) { + return await this.prismaService.business.count( + this.scopeService.scopeFindMany(args, projectIds), + ); + } } diff --git a/services/workflows-service/src/business/business.service.ts b/services/workflows-service/src/business/business.service.ts index 324a81d1db..9c3cfe5d41 100755 --- a/services/workflows-service/src/business/business.service.ts +++ b/services/workflows-service/src/business/business.service.ts @@ -4,17 +4,18 @@ import { TCompanyInformation, } from '@/business/types/business-information'; import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { FEATURE_LIST, TCustomerFeaturesConfig, TCustomerWithFeatures } from '@/customer/types'; import { env } from '@/env'; import type { PrismaTransaction, TProjectIds } from '@/types'; import { HttpService } from '@nestjs/axios'; import * as common from '@nestjs/common'; import { Injectable } from '@nestjs/common'; -import { Business } from '@prisma/client'; +import { Business, Prisma } from '@prisma/client'; import { AxiosError } from 'axios'; import { plainToClass } from 'class-transformer'; +import dayjs from 'dayjs'; import { lastValueFrom } from 'rxjs'; import { BusinessRepository } from './business.repository'; -import { TCustomerWithFeatures } from '@/customer/types'; @Injectable() export class BusinessService { @@ -66,6 +67,90 @@ export class BusinessService { return await this.repository.updateById(id, args, transaction); } + async getMerchantMonitoringMetrics({ + projectIds, + features, + from = dayjs().startOf('month').toISOString(), + to = dayjs(from).add(1, 'month').toISOString(), + }: { + projectIds: string[]; + features: TCustomerWithFeatures['features']; + from: string | undefined; + to: string | undefined; + }): Promise<{ + totalActiveMerchants: number; + addedMerchantsCount: number; + unmonitoredMerchants: number; + }> { + const allProjectMerchants = await this.repository.findMany({}, projectIds); + + const totalActiveMerchants = allProjectMerchants.filter(b => { + const isEnabled = ( + b?.metadata as { + featureConfig: Record< + (typeof FEATURE_LIST)[keyof typeof FEATURE_LIST], + TCustomerFeaturesConfig & { disabledAt: number | null | undefined } + >; + } + )?.featureConfig?.[FEATURE_LIST.ONGOING_MERCHANT_REPORT]?.enabled; + + return ( + isEnabled || + (b.metadata === null && features?.ONGOING_MERCHANT_REPORT?.options?.runByDefault) + ); + }).length; + + const addedMerchantsCount = await this.repository.count( + { + where: { + OR: [ + { + metadata: { + path: ['featureConfig', FEATURE_LIST.ONGOING_MERCHANT_REPORT, 'enabled'], + equals: true, + }, + }, + features?.ONGOING_MERCHANT_REPORT?.options?.runByDefault + ? { metadata: { equals: Prisma.AnyNull } } + : {}, + ], + createdAt: { + gte: dayjs(from).toISOString(), + lt: dayjs(to).toISOString(), + }, + }, + }, + projectIds, + ); + + const unmonitoredMerchants = await this.repository.count( + { + where: { + OR: [ + { + metadata: { + path: ['featureConfig', FEATURE_LIST.ONGOING_MERCHANT_REPORT, 'disabledAt'], + not: 'null', + gte: dayjs(from).toDate().getTime(), + lt: dayjs(to).toDate().getTime(), + }, + }, + !features?.ONGOING_MERCHANT_REPORT?.options?.runByDefault + ? { metadata: { equals: Prisma.AnyNull } } + : {}, + ], + }, + }, + projectIds, + ); + + return { + totalActiveMerchants, + addedMerchantsCount, + unmonitoredMerchants, + }; + } + async fetchCompanyInformation({ registrationNumber, jurisdictionCode,