From 26058ec176d035d995a45204e7c32e85a7c9a6d3 Mon Sep 17 00:00:00 2001 From: Aastha Bist Date: Tue, 7 May 2024 13:58:20 +0530 Subject: [PATCH] feat: add methods to get reports - spender and approver (#2895) --- .../v1/platform-stats-request-param.model.ts | 3 + .../platform/v1/reports-query-params.model.ts | 1 + .../v1/approver/reports.service.spec.ts | 140 +++++++++++++++++- .../platform/v1/approver/reports.service.ts | 67 ++++++++- .../v1/spender/reports.service.spec.ts | 33 +++++ .../platform/v1/spender/reports.service.ts | 21 ++- 6 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 src/app/core/models/platform/v1/platform-stats-request-param.model.ts diff --git a/src/app/core/models/platform/v1/platform-stats-request-param.model.ts b/src/app/core/models/platform/v1/platform-stats-request-param.model.ts new file mode 100644 index 0000000000..484eb2c083 --- /dev/null +++ b/src/app/core/models/platform/v1/platform-stats-request-param.model.ts @@ -0,0 +1,3 @@ +export interface PlatformStatsRequestParams { + state: string; +} diff --git a/src/app/core/models/platform/v1/reports-query-params.model.ts b/src/app/core/models/platform/v1/reports-query-params.model.ts index d1e53cbd71..5cac67b9f0 100644 --- a/src/app/core/models/platform/v1/reports-query-params.model.ts +++ b/src/app/core/models/platform/v1/reports-query-params.model.ts @@ -3,4 +3,5 @@ export interface ReportsQueryParams { offset?: number; limit?: number; order?: string; + id?: string; } diff --git a/src/app/core/services/platform/v1/approver/reports.service.spec.ts b/src/app/core/services/platform/v1/approver/reports.service.spec.ts index 542a5ab864..4afd3887fe 100644 --- a/src/app/core/services/platform/v1/approver/reports.service.spec.ts +++ b/src/app/core/services/platform/v1/approver/reports.service.spec.ts @@ -2,19 +2,35 @@ import { TestBed } from '@angular/core/testing'; import { ApproverReportsService } from './reports.service'; import { ApproverPlatformApiService } from '../../../approver-platform-api.service'; import { of } from 'rxjs'; +import { PAGINATION_SIZE } from 'src/app/constants'; +import { + allReportsPaginated1, + allReportsPaginated2, + expectedReportsPaginated, + expectedReportsSinglePage, + mockQueryParams, + platformReportCountData, +} from 'src/app/core/mock-data/platform-report.data'; +import { ReportsQueryParams } from 'src/app/core/models/platform/v1/reports-query-params.model'; +import { StatsResponse } from 'src/app/core/models/platform/v1/stats-response.model'; describe('ApproverReportsService', () => { let approverReportsService: ApproverReportsService; - const approverPlatformApiService = jasmine.createSpyObj('ApproverPlatformApiService', ['post']); + let approverPlatformApiService: jasmine.SpyObj; beforeEach(() => { + const approverPlatformApiServiceSpy = jasmine.createSpyObj('ApproverPlatformApiService', ['post', 'get']); TestBed.configureTestingModule({ providers: [ ApproverReportsService, - { provide: ApproverPlatformApiService, useValue: approverPlatformApiService }, + { provide: PAGINATION_SIZE, useValue: 2 }, + { provide: ApproverPlatformApiService, useValue: approverPlatformApiServiceSpy }, ], }); approverReportsService = TestBed.inject(ApproverReportsService); + approverPlatformApiService = TestBed.inject( + ApproverPlatformApiService + ) as jasmine.SpyObj; }); it('should be created', () => { @@ -39,4 +55,124 @@ describe('ApproverReportsService', () => { done(); }); }); + + it('getReportsCount(): should get a count of reports', (done) => { + // Mock the response of getReportsByParams + spyOn(approverReportsService, 'getReportsByParams').and.returnValue(of(platformReportCountData)); + + const expectedParams: ReportsQueryParams = { + ...mockQueryParams, + limit: 1, + offset: 0, + }; + + approverReportsService.getReportsCount(mockQueryParams).subscribe((res) => { + // Verify + expect(res).toEqual(4); // Check if the count is as expected + expect(approverReportsService.getReportsByParams).toHaveBeenCalledWith(expectedParams); // Check if the method is called with the expected params + done(); // Call 'done' to indicate the end of the asynchronous test + }); + }); + + it('getAllReportsByParams(): should get all reports multiple pages', (done) => { + const getReportsByParams = spyOn(approverReportsService, 'getReportsByParams'); + spyOn(approverReportsService, 'getReportsCount').and.returnValue(of(4)); + + const expectedParams1: ReportsQueryParams = { + ...mockQueryParams, + limit: 2, + offset: 0, + }; + const expectedParams2: ReportsQueryParams = { + ...mockQueryParams, + limit: 2, + offset: 2, + }; + + getReportsByParams.withArgs(expectedParams1).and.returnValue(of(allReportsPaginated1)); + getReportsByParams.withArgs(expectedParams2).and.returnValue(of(allReportsPaginated2)); + + approverReportsService.getAllReportsByParams(mockQueryParams).subscribe((res) => { + expect(res).toEqual(expectedReportsPaginated); + expect(approverReportsService.getReportsCount).toHaveBeenCalledOnceWith({ + state: 'in.(DRAFT,APPROVER_PENDING,APPROVER_INQUIRY)', + }); + expect(getReportsByParams).toHaveBeenCalledWith(expectedParams1); + expect(getReportsByParams).toHaveBeenCalledWith(expectedParams2); + expect(getReportsByParams).toHaveBeenCalledTimes(2); + done(); + }); + }); + + it('getAllReportsByParams(): should get all reports single page', (done) => { + const getReportsByParams = spyOn(approverReportsService, 'getReportsByParams'); + spyOn(approverReportsService, 'getReportsCount').and.returnValue(of(1)); + + const expectedParams: ReportsQueryParams = { + ...mockQueryParams, + offset: 0, + limit: 2, + }; + + getReportsByParams.withArgs(expectedParams).and.returnValue(of(allReportsPaginated1)); + + approverReportsService.getAllReportsByParams(mockQueryParams).subscribe((res) => { + expect(res).toEqual(expectedReportsSinglePage); + expect(approverReportsService.getReportsCount).toHaveBeenCalledOnceWith(mockQueryParams); + expect(getReportsByParams).toHaveBeenCalledOnceWith(expectedParams); + done(); + }); + }); + + it('should get reports with specified parameters', (done) => { + const queryParams = { + state: 'DRAFT', + offset: 0, + limit: 2, + }; + const expectedConfig = { + params: { + ...queryParams, + }, + }; + approverPlatformApiService.get.and.returnValue(of(allReportsPaginated1)); + approverReportsService.getReportsByParams(queryParams).subscribe((response) => { + expect(response).toEqual(allReportsPaginated1); + expect(approverPlatformApiService.get).toHaveBeenCalledOnceWith('/reports', expectedConfig); + done(); + }); + }); + + it('getReportById(): should get a report by id', () => { + spyOn(approverReportsService, 'getReportsByParams').and.returnValue(of(allReportsPaginated1)); + const queryParams = { + id: 'eq.rpvcIMRMyM3A', + }; + approverReportsService.getReportById('rpvcIMRMyM3A').subscribe((res) => { + expect(res).toEqual(allReportsPaginated1.data[0]); + expect(approverReportsService.getReportsByParams).toHaveBeenCalledOnceWith(queryParams); + }); + }); + + it('getReportsStats(): should get advance request stats', (done) => { + const statsResponse: StatsResponse = { + count: 2, + total_amount: 1200, + }; + approverPlatformApiService.post.and.returnValue(of({ data: statsResponse })); + + const params = { + state: 'eq.DRAFT', + }; + + approverReportsService.getReportsStats(params).subscribe((res) => { + expect(res).toEqual(statsResponse); + expect(approverPlatformApiService.post).toHaveBeenCalledOnceWith('/reports/stats', { + data: { + query_params: `state=${params.state}`, + }, + }); + done(); + }); + }); }); diff --git a/src/app/core/services/platform/v1/approver/reports.service.ts b/src/app/core/services/platform/v1/approver/reports.service.ts index 9003899580..1779dca19a 100644 --- a/src/app/core/services/platform/v1/approver/reports.service.ts +++ b/src/app/core/services/platform/v1/approver/reports.service.ts @@ -1,12 +1,73 @@ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Inject, Injectable } from '@angular/core'; +import { Observable, concatMap, map, range, reduce, switchMap } from 'rxjs'; import { ApproverPlatformApiService } from '../../../approver-platform-api.service'; +import { PlatformApiResponse } from 'src/app/core/models/platform/platform-api-response.model'; +import { ReportsQueryParams } from 'src/app/core/models/platform/v1/reports-query-params.model'; +import { PAGINATION_SIZE } from 'src/app/constants'; +import { Report } from 'src/app/core/models/platform/v1/report.model'; +import { PlatformStatsRequestParams } from 'src/app/core/models/platform/v1/platform-stats-request-param.model'; +import { StatsResponse } from 'src/app/core/models/platform/v1/stats-response.model'; @Injectable({ providedIn: 'root', }) export class ApproverReportsService { - constructor(private approverPlatformApiService: ApproverPlatformApiService) {} + constructor( + @Inject(PAGINATION_SIZE) private paginationSize: number, + private approverPlatformApiService: ApproverPlatformApiService + ) {} + + getAllReportsByParams(queryParams: ReportsQueryParams): Observable { + return this.getReportsCount(queryParams).pipe( + switchMap((count) => { + count = count > this.paginationSize ? count / this.paginationSize : 1; + return range(0, count); + }), + concatMap((page) => { + const params = { + ...queryParams, + offset: this.paginationSize * page, + limit: this.paginationSize, + }; + return this.getReportsByParams(params); + }), + reduce((acc, curr) => acc.concat(curr.data), [] as Report[]) + ); + } + + getReportsCount(queryParams: ReportsQueryParams): Observable { + const params = { + state: queryParams.state, + limit: 1, + offset: 0, + }; + return this.getReportsByParams(params).pipe(map((res) => res.count)); + } + + getReportsByParams(queryParams: ReportsQueryParams = {}): Observable> { + const config = { + params: { + ...queryParams, + }, + }; + return this.approverPlatformApiService.get>('/reports', config); + } + + getReportsStats(params: PlatformStatsRequestParams): Observable { + const queryParams = { + data: { + query_params: `state=${params.state}`, + }, + }; + return this.approverPlatformApiService + .post<{ data: StatsResponse }>('/reports/stats', queryParams) + .pipe(map((res) => res.data)); + } + + getReportById(id: string): Observable { + const queryParams = { id: `eq.${id}` }; + return this.getReportsByParams(queryParams).pipe(map((res: PlatformApiResponse) => res.data[0])); + } ejectExpenses(rptId: string, expenseId: string, comment?: string[]): Observable { const payload = { diff --git a/src/app/core/services/platform/v1/spender/reports.service.spec.ts b/src/app/core/services/platform/v1/spender/reports.service.spec.ts index cbf411cfed..faf4b54911 100644 --- a/src/app/core/services/platform/v1/spender/reports.service.spec.ts +++ b/src/app/core/services/platform/v1/spender/reports.service.spec.ts @@ -153,6 +153,39 @@ describe('SpenderReportsService', () => { }); }); + it('getReportById(): should get a report by id', () => { + spyOn(spenderReportsService, 'getReportsByParams').and.returnValue(of(allReportsPaginated1)); + const queryParams = { + id: 'eq.rpvcIMRMyM3A', + }; + spenderReportsService.getReportById('rpvcIMRMyM3A').subscribe((res) => { + expect(res).toEqual(allReportsPaginated1.data[0]); + expect(spenderReportsService.getReportsByParams).toHaveBeenCalledOnceWith(queryParams); + }); + }); + + it('getReportsStats(): should get advance request stats', (done) => { + const statsResponse = { + count: 2, + total_amount: 1200, + }; + spenderPlatformV1ApiService.post.and.returnValue(of({ data: statsResponse })); + + const params = { + state: 'eq.DRAFT', + }; + + spenderReportsService.getReportsStats(params).subscribe((res) => { + expect(res).toEqual(statsResponse); + expect(spenderPlatformV1ApiService.post).toHaveBeenCalledOnceWith('/reports/stats', { + data: { + query_params: `state=${params.state}`, + }, + }); + done(); + }); + }); + it('ejectExpenses(): should remove an expense from a report', (done) => { spenderPlatformV1ApiService.post.and.returnValue(of(null)); spyOn(spenderReportsService, 'clearTransactionCache').and.returnValue(of(null)); diff --git a/src/app/core/services/platform/v1/spender/reports.service.ts b/src/app/core/services/platform/v1/spender/reports.service.ts index 461262c1a8..e0bee40475 100644 --- a/src/app/core/services/platform/v1/spender/reports.service.ts +++ b/src/app/core/services/platform/v1/spender/reports.service.ts @@ -8,6 +8,8 @@ import { ReportsQueryParams } from 'src/app/core/models/platform/v1/reports-quer import { PAGINATION_SIZE } from 'src/app/constants'; import { CreateDraftParams } from 'src/app/core/models/platform/v1/create-draft-params.model'; import { PlatformApiPayload } from 'src/app/core/models/platform/platform-api-payload.model'; +import { StatsResponse } from 'src/app/core/models/platform/v1/stats-response.model'; +import { PlatformStatsRequestParams } from 'src/app/core/models/platform/v1/platform-stats-request-param.model'; import { CacheBuster } from 'ts-cacheable'; import { UserEventService } from '../../../user-event.service'; import { TransactionService } from '../../../transaction.service'; @@ -79,10 +81,11 @@ export class SpenderReportsService { }), concatMap((page) => { const params = { - state: queryParams.state, + ...queryParams, offset: this.paginationSize * page, limit: this.paginationSize, }; + return this.getReportsByParams(params); }), reduce((acc, curr) => acc.concat(curr.data), [] as Report[]) @@ -107,9 +110,25 @@ export class SpenderReportsService { return this.spenderPlatformV1ApiService.get>('/reports', config); } + getReportById(id: string): Observable { + const queryParams = { id: `eq.${id}` }; + return this.getReportsByParams(queryParams).pipe(map((res: PlatformApiResponse) => res.data[0])); + } + createDraft(data: CreateDraftParams): Observable { return this.spenderPlatformV1ApiService .post>('/reports', data) .pipe(map((res) => res.data)); } + + getReportsStats(params: PlatformStatsRequestParams): Observable { + const queryParams = { + data: { + query_params: `state=${params.state}`, + }, + }; + return this.spenderPlatformV1ApiService + .post<{ data: StatsResponse }>('/reports/stats', queryParams) + .pipe(map((res) => res.data)); + } }