|
1 | 1 | import * as common from '@nestjs/common'; |
2 | 2 | import * as swagger from '@nestjs/swagger'; |
3 | | -import { ApiBearerAuth } from '@nestjs/swagger'; |
| 3 | +import { ApiBearerAuth, ApiConsumes } from '@nestjs/swagger'; |
4 | 4 | import * as errors from '@/errors'; |
5 | | -import { PrismaService } from '@/prisma/prisma.service'; |
6 | | -import { UseGuards } from '@nestjs/common'; |
| 5 | +import { |
| 6 | + BadRequestException, |
| 7 | + Body, |
| 8 | + Param, |
| 9 | + Query, |
| 10 | + Res, |
| 11 | + UploadedFile, |
| 12 | + UseGuards, |
| 13 | + UseInterceptors, |
| 14 | +} from '@nestjs/common'; |
7 | 15 | import { AdminAuthGuard } from '@/common/guards/admin-auth.guard'; |
| 16 | +import { BusinessReportService } from '@/business-report/business-report.service'; |
| 17 | +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; |
| 18 | +import { CustomerService } from '@/customer/customer.service'; |
| 19 | +import { BusinessService } from '@/business/business.service'; |
| 20 | +import { CurrentProject } from '@/common/decorators/current-project.decorator'; |
| 21 | +import type { TProjectId } from '@/types'; |
| 22 | +import { GetLatestBusinessReportDto } from '@/business-report/get-latest-business-report.dto'; |
| 23 | +import { |
| 24 | + BusinessReportListRequestParamDto, |
| 25 | + BusinessReportListResponseDto, |
| 26 | + ListBusinessReportsSchema, |
| 27 | +} from '@/business-report/business-report-list.dto'; |
| 28 | +import { ZodValidationPipe } from '@/common/pipes/zod.pipe'; |
| 29 | +import { CreateBusinessReportDto } from '@/business-report/dto/create-business-report.dto'; |
| 30 | +import { Business } from '@prisma/client'; |
| 31 | +import { BusinessReportDto } from '@/business-report/business-report.dto'; |
| 32 | +import { FileInterceptor } from '@nestjs/platform-express'; |
| 33 | +import { getDiskStorage } from '@/storage/get-file-storage-manager'; |
| 34 | +import { fileFilter } from '@/storage/file-filter'; |
| 35 | +import { RemoveTempFileInterceptor } from '@/common/interceptors/remove-temp-file.interceptor'; |
| 36 | +import { CreateBusinessReportBatchBodyDto } from '@/business-report/dto/create-business-report-batch-body.dto'; |
| 37 | +import type { Response } from 'express'; |
| 38 | +import { PrismaService } from '@/prisma/prisma.service'; |
8 | 39 |
|
9 | 40 | @ApiBearerAuth() |
10 | 41 | @swagger.ApiTags('Business Reports') |
11 | 42 | @common.Controller('external/business-reports') |
12 | | -@swagger.ApiExcludeController() |
13 | 43 | export class BusinessReportControllerExternal { |
14 | | - constructor(private readonly prisma: PrismaService) {} |
| 44 | + constructor( |
| 45 | + protected readonly businessReportService: BusinessReportService, |
| 46 | + protected readonly logger: AppLoggerService, |
| 47 | + protected readonly customerService: CustomerService, |
| 48 | + protected readonly businessService: BusinessService, |
| 49 | + private readonly prisma: PrismaService, |
| 50 | + ) {} |
| 51 | + |
| 52 | + @common.Get('/latest') |
| 53 | + @swagger.ApiOkResponse({ type: [String] }) |
| 54 | + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 55 | + @swagger.ApiExcludeEndpoint() |
| 56 | + async getLatestBusinessReport( |
| 57 | + @CurrentProject() currentProjectId: TProjectId, |
| 58 | + @Query() { businessId, type }: GetLatestBusinessReportDto, |
| 59 | + ) { |
| 60 | + const { id: customerId } = await this.customerService.getByProjectId(currentProjectId); |
| 61 | + |
| 62 | + const latestReport = await this.businessReportService.findLatest({ |
| 63 | + businessId, |
| 64 | + customerId, |
| 65 | + reportType: type, |
| 66 | + }); |
| 67 | + |
| 68 | + return latestReport ?? {}; |
| 69 | + } |
| 70 | + |
| 71 | + @common.Get() |
| 72 | + @swagger.ApiOkResponse({ type: BusinessReportListResponseDto }) |
| 73 | + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 74 | + @common.UsePipes(new ZodValidationPipe(ListBusinessReportsSchema, 'query')) |
| 75 | + async listBusinessReports( |
| 76 | + @CurrentProject() currentProjectId: TProjectId, |
| 77 | + @Query() { businessId, page, search }: BusinessReportListRequestParamDto, |
| 78 | + ) { |
| 79 | + const { id: customerId } = await this.customerService.getByProjectId(currentProjectId); |
| 80 | + |
| 81 | + return await this.businessReportService.findMany({ |
| 82 | + withoutUnpublishedOngoingReports: true, |
| 83 | + limit: page.size, |
| 84 | + page: page.number, |
| 85 | + customerId: customerId, |
| 86 | + ...(businessId ? { businessId } : {}), |
| 87 | + ...(search ? { searchQuery: search } : {}), |
| 88 | + }); |
| 89 | + } |
| 90 | + |
| 91 | + @common.Post() |
| 92 | + @swagger.ApiOkResponse({}) |
| 93 | + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 94 | + async createBusinessReport( |
| 95 | + @Body() |
| 96 | + { |
| 97 | + websiteUrl, |
| 98 | + countryCode, |
| 99 | + merchantName, |
| 100 | + businessCorrelationId, |
| 101 | + reportType, |
| 102 | + workflowVersion, |
| 103 | + }: CreateBusinessReportDto, |
| 104 | + @CurrentProject() currentProjectId: TProjectId, |
| 105 | + ) { |
| 106 | + const { id: customerId, config } = await this.customerService.getByProjectId(currentProjectId); |
| 107 | + |
| 108 | + const { maxBusinessReports, withQualityControl } = config || {}; |
| 109 | + await this.businessReportService.checkBusinessReportsLimit(maxBusinessReports, customerId); |
| 110 | + |
| 111 | + let business: Pick<Business, 'id' | 'correlationId'> | undefined; |
| 112 | + const merchantNameWithDefault = merchantName || 'Not detected'; |
| 113 | + |
| 114 | + if (!businessCorrelationId) { |
| 115 | + business = await this.businessService.create({ |
| 116 | + data: { |
| 117 | + companyName: merchantNameWithDefault, |
| 118 | + country: countryCode, |
| 119 | + website: websiteUrl, |
| 120 | + projectId: currentProjectId, |
| 121 | + }, |
| 122 | + select: { |
| 123 | + id: true, |
| 124 | + correlationId: true, |
| 125 | + }, |
| 126 | + }); |
| 127 | + } |
| 128 | + |
| 129 | + if (businessCorrelationId) { |
| 130 | + business = |
| 131 | + (await this.businessService.getByCorrelationId(businessCorrelationId, [currentProjectId], { |
| 132 | + select: { |
| 133 | + id: true, |
| 134 | + correlationId: true, |
| 135 | + }, |
| 136 | + })) ?? undefined; |
| 137 | + } |
| 138 | + |
| 139 | + if (!business) { |
| 140 | + throw new BadRequestException( |
| 141 | + `Business with an id of ${businessCorrelationId} was not found`, |
| 142 | + ); |
| 143 | + } |
| 144 | + |
| 145 | + await this.businessReportService.createBusinessReportAndTriggerReportCreation({ |
| 146 | + reportType, |
| 147 | + business, |
| 148 | + websiteUrl, |
| 149 | + countryCode, |
| 150 | + merchantName: merchantNameWithDefault, |
| 151 | + workflowVersion, |
| 152 | + withQualityControl, |
| 153 | + customerId, |
| 154 | + }); |
| 155 | + } |
| 156 | + |
| 157 | + @common.Get(':id') |
| 158 | + @swagger.ApiOkResponse({ type: BusinessReportDto }) |
| 159 | + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 160 | + @common.UsePipes(new ZodValidationPipe(ListBusinessReportsSchema, 'query')) |
| 161 | + async getBusinessReportById( |
| 162 | + @CurrentProject() currentProjectId: TProjectId, |
| 163 | + @Param('id') id: string, |
| 164 | + ) { |
| 165 | + const { id: customerId } = await this.customerService.getByProjectId(currentProjectId); |
| 166 | + |
| 167 | + return await this.businessReportService.findById({ id, customerId }); |
| 168 | + } |
| 169 | + |
| 170 | + @swagger.ApiExcludeEndpoint() |
| 171 | + @common.Post('/upload-batch') |
| 172 | + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 173 | + @ApiConsumes('multipart/form-data') |
| 174 | + @UseInterceptors( |
| 175 | + FileInterceptor('file', { |
| 176 | + storage: getDiskStorage(), |
| 177 | + fileFilter, |
| 178 | + }), |
| 179 | + RemoveTempFileInterceptor, |
| 180 | + ) |
| 181 | + async createBusinessReportBatch( |
| 182 | + @UploadedFile() file: Express.Multer.File, |
| 183 | + @Body() { type, workflowVersion }: CreateBusinessReportBatchBodyDto, |
| 184 | + @Res() res: Response, |
| 185 | + @CurrentProject() currentProjectId: TProjectId, |
| 186 | + ) { |
| 187 | + const { id: customerId, config } = await this.customerService.getByProjectId(currentProjectId); |
| 188 | + |
| 189 | + const { maxBusinessReports, withQualityControl } = config || {}; |
| 190 | + await this.businessReportService.checkBusinessReportsLimit(maxBusinessReports, customerId); |
| 191 | + |
| 192 | + const result = await this.businessReportService.processBatchFile({ |
| 193 | + type, |
| 194 | + workflowVersion, |
| 195 | + customerId, |
| 196 | + maxBusinessReports, |
| 197 | + merchantSheet: file, |
| 198 | + projectId: currentProjectId, |
| 199 | + withQualityControl: typeof withQualityControl === 'boolean' ? withQualityControl : false, |
| 200 | + }); |
| 201 | + |
| 202 | + res.status(201); |
| 203 | + res.setHeader('content-type', 'application/json'); |
| 204 | + res.send(result); |
| 205 | + } |
15 | 206 |
|
16 | 207 | @common.Get() |
17 | 208 | @UseGuards(AdminAuthGuard) |
18 | 209 | @swagger.ApiOkResponse({ type: [String] }) |
19 | 210 | @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) |
| 211 | + @swagger.ApiExcludeEndpoint() |
20 | 212 | async list() { |
21 | 213 | return await this.prisma.businessReport.findMany({ |
22 | 214 | include: { |
|
0 commit comments