diff --git a/apps/backoffice-v2/src/pages/Home/Home.page.tsx b/apps/backoffice-v2/src/pages/Home/Home.page.tsx index c4b6160ad..3b11617b0 100644 --- a/apps/backoffice-v2/src/pages/Home/Home.page.tsx +++ b/apps/backoffice-v2/src/pages/Home/Home.page.tsx @@ -55,8 +55,9 @@ export const Home: FunctionComponent = () => { {/* */} {/* */} {/**/} - {(isDemo || isExample) && } - {!isDemo && !isExample && } + + {/* {(isDemo || isExample) && } + {!isDemo && !isExample && } */} ); 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 1817d3d3b..6aab84877 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 @@ -44,7 +44,6 @@ import { } from '@/business-report/dtos/business-report-metrics.dto'; import { BusinessReportMetricsDto } from './dtos/business-report-metrics-dto'; import { FEATURE_LIST, TCustomerWithFeatures } from '@/customer/types'; -import dayjs from 'dayjs'; @ApiBearerAuth() @swagger.ApiTags('Business Reports') @@ -167,19 +166,17 @@ 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, + ); - const unmonitoredMerchants = await this.prismaService.business.count({ - where: { - projectId: currentProjectId, - metadata: { - path: ['featureConfig', FEATURE_LIST.ONGOING_MERCHANT_REPORT, 'disabledAt'], - not: 'null', - ...(from && { gt: dayjs(from).toDate().getTime() }), - ...(to && { lte: dayjs(to).toDate().getTime() }), - }, - }, - }); + const { totalActiveMerchants, addedMerchantsCount, unmonitoredMerchants } = + await this.businessService.getMerchantMonitoringMetrics({ + projectIds: [currentProjectId], + features, + from, + to, + }); const merchantMonitoringMetrics = await this.merchantMonitoringClient.getMetrics({ customerId, @@ -189,6 +186,8 @@ export class BusinessReportControllerExternal { return { ...merchantMonitoringMetrics, + totalActiveMerchants, + addedMerchantsCount, removedMerchantsCount: unmonitoredMerchants, }; } diff --git a/services/workflows-service/src/business/business.repository.ts b/services/workflows-service/src/business/business.repository.ts index 1e0026371..4dc2c6555 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 324a81d1d..376e907c1 100755 --- a/services/workflows-service/src/business/business.service.ts +++ b/services/workflows-service/src/business/business.service.ts @@ -4,17 +4,19 @@ 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 { z } from 'zod'; import { BusinessRepository } from './business.repository'; -import { TCustomerWithFeatures } from '@/customer/types'; @Injectable() export class BusinessService { @@ -66,6 +68,105 @@ export class BusinessService { return await this.repository.updateById(id, args, transaction); } + async getMerchantMonitoringMetrics({ + projectIds, + features, + from, + to, + }: { + projectIds: string[]; + features: TCustomerWithFeatures['features']; + from: string | undefined; + to: string | undefined; + }): Promise<{ + totalActiveMerchants: number; + addedMerchantsCount: number; + unmonitoredMerchants: number; + }> { + // Metrics are currently mostly requested by month + if (!from) { + from = dayjs().startOf('month').toISOString(); + } + + if (!to) { + to = dayjs(from).add(1, 'month').toISOString(); + } + + const allProjectMerchants = await this.repository.findMany({}, projectIds); + + const totalActiveMerchants = allProjectMerchants.filter(b => { + const disabledAt = z + .number() + .nullable() + .catch(() => null) + .parse( + ( + b.metadata as { + featureConfig: Record< + (typeof FEATURE_LIST)[keyof typeof FEATURE_LIST], + TCustomerFeaturesConfig & { disabledAt: number | null | undefined } + >; + } + )?.featureConfig?.[FEATURE_LIST.ONGOING_MERCHANT_REPORT]?.disabledAt, + ); + + return ( + disabledAt === null || + (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, 'disabledAt'], + equals: Prisma.AnyNull, + }, + }, + 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,