From a63aa8f90c785571595c855bdcb175570a770714 Mon Sep 17 00:00:00 2001 From: Arjun Date: Wed, 4 Oct 2023 11:00:44 +0530 Subject: [PATCH] feat: Mileage/ Per Diem expenses creation to depend on category (#2461) * initial changes for dashboard * done * fix failing tests * pr comment fi * added categories service spy to tests * minor * remove unwanted changes * minor * remove fdescribe * test: Mileage/ Per Diem expenses to depend on category (#2468) * coverage * test description update * fix tests * fix * more tests --- .../mock-data/action-sheet-options.data.ts | 42 +++++++++++++++++ .../mock-data/allowed-expense-types.data.ts | 4 ++ src/app/core/mock-data/org-category.data.ts | 28 +++++++++++ .../core/services/categories.service.spec.ts | 17 +++++++ src/app/core/services/categories.service.ts | 13 +++++ src/app/fyle/dashboard/dashboard.page.html | 13 +++-- src/app/fyle/dashboard/dashboard.page.scss | 7 +++ src/app/fyle/dashboard/dashboard.page.spec.ts | 45 +++++++++++++++--- src/app/fyle/dashboard/dashboard.page.ts | 27 ++++++++--- .../fyle/my-expenses/my-expenses.page.html | 15 ++++-- .../fyle/my-expenses/my-expenses.page.scss | 7 +++ .../fyle/my-expenses/my-expenses.page.spec.ts | 47 ++++++++++++++++--- src/app/fyle/my-expenses/my-expenses.page.ts | 36 ++++++++++---- 13 files changed, 265 insertions(+), 36 deletions(-) create mode 100644 src/app/core/mock-data/allowed-expense-types.data.ts diff --git a/src/app/core/mock-data/action-sheet-options.data.ts b/src/app/core/mock-data/action-sheet-options.data.ts index f8b0d47f42..59c080b37f 100644 --- a/src/app/core/mock-data/action-sheet-options.data.ts +++ b/src/app/core/mock-data/action-sheet-options.data.ts @@ -46,3 +46,45 @@ export const expectedActionSheetButtonRes = [ handler: undefined, }, ]; + +export const expectedActionSheetButtonsWithMileage = [ + { + text: 'Capture Receipt', + icon: 'assets/svg/fy-camera.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, + { + text: 'Add Manually', + icon: 'assets/svg/fy-expense.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, + { + text: 'Add Mileage', + icon: 'assets/svg/fy-mileage.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, +]; + +export const expectedActionSheetButtonsWithPerDiem = [ + { + text: 'Capture Receipt', + icon: 'assets/svg/fy-camera.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, + { + text: 'Add Manually', + icon: 'assets/svg/fy-expense.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, + { + text: 'Add Per Diem', + icon: 'assets/svg/fy-calendar.svg', + cssClass: 'capture-receipt', + handler: undefined, + }, +]; diff --git a/src/app/core/mock-data/allowed-expense-types.data.ts b/src/app/core/mock-data/allowed-expense-types.data.ts new file mode 100644 index 0000000000..dd41359380 --- /dev/null +++ b/src/app/core/mock-data/allowed-expense-types.data.ts @@ -0,0 +1,4 @@ +export const allowedExpenseTypes: Record = { + mileage: true, + perDiem: true, +}; diff --git a/src/app/core/mock-data/org-category.data.ts b/src/app/core/mock-data/org-category.data.ts index 9e69cfa385..1c42ded879 100644 --- a/src/app/core/mock-data/org-category.data.ts +++ b/src/app/core/mock-data/org-category.data.ts @@ -1,3 +1,4 @@ +import { PlatformCategory } from '../models/platform/platform-category.model'; import { OrgCategory } from '../models/v1/org-category.model'; export const orgCategoryData: OrgCategory = { @@ -13,6 +14,33 @@ export const orgCategoryData: OrgCategory = { updated_at: new Date('2022-05-05T17:45:42.092507+00:00'), }; +export const mileagePerDiemPlatformCategoryData: PlatformCategory[] = [ + { + code: null, + created_at: new Date('2018-01-31T23:50:27.235056+00:00'), + display_name: 'Mileage', + is_enabled: true, + system_category: 'Mileage', + id: 16566, + name: 'Mileage', + org_id: 'orNVthTo2Zyo', + sub_category: 'Mileage', + updated_at: new Date('2022-05-05T17:45:42.092507+00:00'), + }, + { + code: null, + created_at: new Date('2018-01-31T23:50:27.235056+00:00'), + display_name: 'Per Diem', + is_enabled: true, + system_category: 'Per Diem', + id: 16566, + name: 'Per Diem', + org_id: 'orNVthTo2Zyo', + sub_category: 'Per Diem', + updated_at: new Date('2022-05-05T17:45:42.092507+00:00'), + }, +]; + export const transformedOrgCategories: OrgCategory[] = [ { code: '93', diff --git a/src/app/core/services/categories.service.spec.ts b/src/app/core/services/categories.service.spec.ts index 080c14899a..cf2350a90d 100644 --- a/src/app/core/services/categories.service.spec.ts +++ b/src/app/core/services/categories.service.spec.ts @@ -96,6 +96,23 @@ describe('CategoriesService', () => { }); }); + it('getMileageOrPerDiemCategories(): should get platform categories with Mileage and Per Diem as system category', (done) => { + spenderPlatformV1ApiService.get.and.returnValue(of(platformApiCategoryRes)); + + const apiParam = { + params: { + is_enabled: 'eq.true', + system_category: 'in.(Mileage, Per Diem)', + }, + }; + + categoriesService.getMileageOrPerDiemCategories().subscribe((res) => { + expect(res).toEqual(platformApiCategoryRes.data); + expect(spenderPlatformV1ApiService.get).toHaveBeenCalledOnceWith('/categories', apiParam); + done(); + }); + }); + it('getCategories(): should get categories from the api', (done) => { spenderPlatformV1ApiService.get.and.returnValue(of(platformApiAllCategories)); spyOn(categoriesService, 'transformFrom').and.returnValue(transformedOrgCategories); diff --git a/src/app/core/services/categories.service.ts b/src/app/core/services/categories.service.ts index da91966656..0df6eecec6 100644 --- a/src/app/core/services/categories.service.ts +++ b/src/app/core/services/categories.service.ts @@ -78,6 +78,19 @@ export class CategoriesService { ); } + @Cacheable() + getMileageOrPerDiemCategories(): Observable { + const data = { + params: { + is_enabled: 'eq.true', + system_category: 'in.(Mileage, Per Diem)', + }, + }; + return this.spenderPlatformV1ApiService + .get>('/categories', data) + .pipe(map((res) => res.data)); + } + transformFrom(platformCategory: PlatformCategory[]): OrgCategory[] { const oldCategory = platformCategory.map((category) => ({ code: category.code, diff --git a/src/app/fyle/dashboard/dashboard.page.html b/src/app/fyle/dashboard/dashboard.page.html index 1fae44bfd6..fba456ebbc 100644 --- a/src/app/fyle/dashboard/dashboard.page.html +++ b/src/app/fyle/dashboard/dashboard.page.html @@ -30,9 +30,16 @@ Home -
- -
+ +
+ +
+
+ +
+ +
+
diff --git a/src/app/fyle/dashboard/dashboard.page.scss b/src/app/fyle/dashboard/dashboard.page.scss index 9a1a4c3134..f3ea25047a 100644 --- a/src/app/fyle/dashboard/dashboard.page.scss +++ b/src/app/fyle/dashboard/dashboard.page.scss @@ -60,5 +60,12 @@ &--action-shortcut { padding-right: 12px; padding-top: 2px; + + &--skeleton-icon { + margin-right: 12px; + width: 24px; + height: 24px; + border-radius: 5px; + } } } diff --git a/src/app/fyle/dashboard/dashboard.page.spec.ts b/src/app/fyle/dashboard/dashboard.page.spec.ts index 9c0cefaa32..3bdaab90e1 100644 --- a/src/app/fyle/dashboard/dashboard.page.spec.ts +++ b/src/app/fyle/dashboard/dashboard.page.spec.ts @@ -17,9 +17,16 @@ import { Subject, Subscription, of } from 'rxjs'; import { orgSettingsRes } from 'src/app/core/mock-data/org-settings.data'; import { orgUserSettingsData } from 'src/app/core/mock-data/org-user-settings.data'; import { BackButtonActionPriority } from 'src/app/core/models/back-button-action-priority.enum'; -import { cloneDeep } from 'lodash'; -import { expectedActionSheetButtonRes } from 'src/app/core/mock-data/action-sheet-options.data'; +import { clone, cloneDeep } from 'lodash'; +import { + expectedActionSheetButtonRes, + expectedActionSheetButtonsWithMileage, + expectedActionSheetButtonsWithPerDiem, +} from 'src/app/core/mock-data/action-sheet-options.data'; import { creditTxnFilterPill } from 'src/app/core/mock-data/filter-pills.data'; +import { allowedExpenseTypes } from 'src/app/core/mock-data/allowed-expense-types.data'; +import { CategoriesService } from 'src/app/core/services/categories.service'; +import { mileagePerDiemPlatformCategoryData } from 'src/app/core/mock-data/org-category.data'; describe('DashboardPage', () => { let component: DashboardPage; @@ -34,6 +41,7 @@ describe('DashboardPage', () => { let smartlookService: jasmine.SpyObj; let orgSettingsService: jasmine.SpyObj; let orgUserSettingsService: jasmine.SpyObj; + let categoriesService: jasmine.SpyObj; let backButtonService: jasmine.SpyObj; let platform: Platform; let navController: jasmine.SpyObj; @@ -53,6 +61,7 @@ describe('DashboardPage', () => { let smartlookServiceSpy = jasmine.createSpyObj('SmartlookService', ['init']); let orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); let orgUserSettingsServiceSpy = jasmine.createSpyObj('OrgUserSettingsService', ['get']); + const categoriesServiceSpy = jasmine.createSpyObj('CategoriesService', ['getMileageOrPerDiemCategories']); let backButtonServiceSpy = jasmine.createSpyObj('BackButtonService', ['showAppCloseAlert']); let navControllerSpy = jasmine.createSpyObj('NavController', ['back']); @@ -69,6 +78,7 @@ describe('DashboardPage', () => { { provide: SmartlookService, useValue: smartlookServiceSpy }, { provide: OrgSettingsService, useValue: orgSettingsServiceSpy }, { provide: OrgUserSettingsService, useValue: orgUserSettingsServiceSpy }, + { provide: CategoriesService, useValue: categoriesServiceSpy }, { provide: BackButtonService, useValue: backButtonServiceSpy }, { provide: NavController, useValue: navControllerSpy }, Platform, @@ -98,6 +108,7 @@ describe('DashboardPage', () => { smartlookService = TestBed.inject(SmartlookService) as jasmine.SpyObj; orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; orgUserSettingsService = TestBed.inject(OrgUserSettingsService) as jasmine.SpyObj; + categoriesService = TestBed.inject(CategoriesService) as jasmine.SpyObj; backButtonService = TestBed.inject(BackButtonService) as jasmine.SpyObj; platform = TestBed.inject(Platform); navController = TestBed.inject(NavController) as jasmine.SpyObj; @@ -171,6 +182,7 @@ describe('DashboardPage', () => { spyOn(component, 'registerBackButtonAction'); orgSettingsService.get.and.returnValue(of(orgSettingsRes)); orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); + categoriesService.getMileageOrPerDiemCategories.and.returnValue(of(mileagePerDiemPlatformCategoryData)); currencyService.getHomeCurrency.and.returnValue(of('USD')); spyOn(component, 'setupActionSheet'); const statsComponentSpy = jasmine.createSpyObj('StatsComponent', ['init']); @@ -228,7 +240,7 @@ describe('DashboardPage', () => { it('should call setupActionSheet once with orgSettings data', () => { component.ionViewWillEnter(); - expect(component.setupActionSheet).toHaveBeenCalledOnceWith(orgSettingsRes); + expect(component.setupActionSheet).toHaveBeenCalledOnceWith(orgSettingsRes, allowedExpenseTypes); }); it('should call init method of statsComponent and tasksComponent', () => { @@ -416,13 +428,32 @@ describe('DashboardPage', () => { }); }); - it('setupActionSheet(): should setup actionSheetButtons', () => { + describe('setupActionSheet()', () => { const mockOrgSettings = cloneDeep(orgSettingsRes); - spyOn(component, 'actionSheetButtonsHandler'); mockOrgSettings.per_diem.enabled = true; mockOrgSettings.mileage.enabled = true; - component.setupActionSheet(mockOrgSettings); - expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonRes); + + it('should setup actionSheetButtons', () => { + spyOn(component, 'actionSheetButtonsHandler'); + component.setupActionSheet(orgSettingsRes, allowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonRes); + }); + + it('should update actionSheetButtons without mileage', () => { + spyOn(component, 'actionSheetButtonsHandler'); + const mockAllowedExpenseTypes = clone(allowedExpenseTypes); + mockAllowedExpenseTypes.mileage = false; + component.setupActionSheet(orgSettingsRes, mockAllowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonsWithPerDiem); + }); + + it('should update actionSheetButtons without Per Diem', () => { + spyOn(component, 'actionSheetButtonsHandler'); + const mockAllowedExpenseTypes = clone(allowedExpenseTypes); + mockAllowedExpenseTypes.perDiem = false; + component.setupActionSheet(orgSettingsRes, mockAllowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonsWithMileage); + }); }); it('openAddExpenseActionSheet(): should open actionSheetController and track event', fakeAsync(() => { diff --git a/src/app/fyle/dashboard/dashboard.page.ts b/src/app/fyle/dashboard/dashboard.page.ts index 6cc8f2e094..2d8540f3b0 100644 --- a/src/app/fyle/dashboard/dashboard.page.ts +++ b/src/app/fyle/dashboard/dashboard.page.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, ViewChild } from '@angular/core'; -import { concat, Observable, of, Subject, Subscription } from 'rxjs'; +import { concat, forkJoin, Observable, of, Subject, Subscription } from 'rxjs'; import { shareReplay, switchMap, takeUntil } from 'rxjs/operators'; import { ActionSheetButton, ActionSheetController, NavController, Platform } from '@ionic/angular'; import { NetworkService } from '../../core/services/network.service'; @@ -19,6 +19,8 @@ import { BackButtonService } from 'src/app/core/services/back-button.service'; import { OrgSettings } from 'src/app/core/models/org-settings.model'; import { FilterPill } from 'src/app/shared/components/fy-filter-pills/filter-pill.interface'; import { CardStatsComponent } from './card-stats/card-stats.component'; +import { PlatformCategory } from 'src/app/core/models/platform/platform-category.model'; +import { CategoriesService } from 'src/app/core/services/categories.service'; enum DashboardState { home, @@ -41,6 +43,8 @@ export class DashboardPage { orgSettings$: Observable; + specialCategories$: Observable; + homeCurrency$: Observable; isConnected$: Observable; @@ -66,6 +70,7 @@ export class DashboardPage { private smartlookService: SmartlookService, private orgUserSettingsService: OrgUserSettingsService, private orgSettingsService: OrgSettingsService, + private categoriesService: CategoriesService, private platform: Platform, private backButtonService: BackButtonService, private navController: NavController @@ -116,10 +121,18 @@ export class DashboardPage { this.orgUserSettings$ = this.orgUserSettingsService.get().pipe(shareReplay(1)); this.orgSettings$ = this.orgSettingsService.get().pipe(shareReplay(1)); + this.specialCategories$ = this.categoriesService.getMileageOrPerDiemCategories().pipe(shareReplay(1)); this.homeCurrency$ = this.currencyService.getHomeCurrency().pipe(shareReplay(1)); - this.orgSettings$.subscribe((orgSettings) => { - this.setupActionSheet(orgSettings); + forkJoin({ + orgSettings: this.orgSettings$, + specialCategories: this.specialCategories$, + }).subscribe(({ orgSettings, specialCategories }) => { + const allowedExpenseTypes = { + mileage: specialCategories.some((category) => category.system_category === 'Mileage'), + perDiem: specialCategories.some((category) => category.system_category === 'Per Diem'), + }; + this.setupActionSheet(orgSettings, allowedExpenseTypes); }); this.statsComponent.init(); @@ -229,10 +242,10 @@ export class DashboardPage { }; } - setupActionSheet(orgSettings: OrgSettings): void { + setupActionSheet(orgSettings: OrgSettings, allowedExpenseTypes: Record): void { const that = this; - const mileageEnabled = orgSettings.mileage.enabled; - const isPerDiemEnabled = orgSettings.per_diem.enabled; + const mileageEnabled = orgSettings.mileage.enabled && allowedExpenseTypes.mileage; + const isPerDiemEnabled = orgSettings.per_diem.enabled && allowedExpenseTypes.perDiem; that.actionSheetButtons = [ { text: 'Capture Receipt', @@ -249,7 +262,7 @@ export class DashboardPage { ]; if (mileageEnabled) { - this.actionSheetButtons.push({ + that.actionSheetButtons.push({ text: 'Add Mileage', icon: 'assets/svg/fy-mileage.svg', cssClass: 'capture-receipt', diff --git a/src/app/fyle/my-expenses/my-expenses.page.html b/src/app/fyle/my-expenses/my-expenses.page.html index c9ba3c0ad1..04e2d89f44 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.html +++ b/src/app/fyle/my-expenses/my-expenses.page.html @@ -19,9 +19,18 @@ - - - + + + + + + + +
+ +
+
+
diff --git a/src/app/fyle/my-expenses/my-expenses.page.scss b/src/app/fyle/my-expenses/my-expenses.page.scss index 9219913334..48399ec77e 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.scss +++ b/src/app/fyle/my-expenses/my-expenses.page.scss @@ -10,6 +10,13 @@ --padding-end: 8px !important; } + &--header-btn--skeleton-loader { + margin: 12px 8px; + border-radius: 5px; + width: 24px; + height: 24px; + } + &--filter-pills { position: fixed; width: 100%; diff --git a/src/app/fyle/my-expenses/my-expenses.page.spec.ts b/src/app/fyle/my-expenses/my-expenses.page.spec.ts index fe8dabbc06..d345546c67 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.spec.ts +++ b/src/app/fyle/my-expenses/my-expenses.page.spec.ts @@ -66,8 +66,12 @@ import { ExpenseFilters } from './expense-filters.model'; import { txnData2, txnList } from 'src/app/core/mock-data/transaction.data'; import { unformattedTxnData } from 'src/app/core/mock-data/unformatted-transaction.data'; import { expenseFiltersData1, expenseFiltersData2 } from 'src/app/core/mock-data/expense-filters.data'; -import { expectedActionSheetButtonRes } from 'src/app/core/mock-data/action-sheet-options.data'; -import { cloneDeep } from 'lodash'; +import { + expectedActionSheetButtonRes, + expectedActionSheetButtonsWithMileage, + expectedActionSheetButtonsWithPerDiem, +} from 'src/app/core/mock-data/action-sheet-options.data'; +import { clone, cloneDeep } from 'lodash'; import { apiAuthRes } from 'src/app/core/mock-data/auth-reponse.data'; import { LoaderService } from 'src/app/core/services/loader.service'; import { PopupService } from 'src/app/core/services/popup.service'; @@ -104,6 +108,9 @@ import { FyDeleteDialogComponent } from 'src/app/shared/components/fy-delete-dia import { getElementRef } from 'src/app/core/dom-helpers'; import { transactionDatum1, transactionDatum3 } from 'src/app/core/mock-data/stats-response.data'; import { uniqueCardsParam } from 'src/app/core/mock-data/unique-cards.data'; +import { allowedExpenseTypes } from 'src/app/core/mock-data/allowed-expense-types.data'; +import { CategoriesService } from 'src/app/core/services/categories.service'; +import { mileagePerDiemPlatformCategoryData } from 'src/app/core/mock-data/org-category.data'; describe('MyExpensesPage', () => { let component: MyExpensesPage; @@ -128,6 +135,7 @@ describe('MyExpensesPage', () => { let storageService: jasmine.SpyObj; let corporateCreditCardService: jasmine.SpyObj; let orgUserSettingsService: jasmine.SpyObj; + let categoriesService: jasmine.SpyObj; let platformHandlerService: jasmine.SpyObj; let trackingService: jasmine.SpyObj; let modalController: jasmine.SpyObj; @@ -176,6 +184,7 @@ describe('MyExpensesPage', () => { 'deleteBulk', ]); const orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); + const categoriesServiceSpy = jasmine.createSpyObj('CategoriesService', ['getMileageOrPerDiemCategories']); const navControllerSpy = jasmine.createSpyObj('NavController', ['back']); const networkServiceSpy = jasmine.createSpyObj('NetworkService', ['isOnline', 'connectivityWatcher']); const activatedRouteSpy = { @@ -326,6 +335,10 @@ describe('MyExpensesPage', () => { provide: SnackbarPropertiesService, useValue: snackbarPropertiesSpy, }, + { + provide: CategoriesService, + useValue: categoriesServiceSpy, + }, ReportState, MaskNumber, ], @@ -344,6 +357,7 @@ describe('MyExpensesPage', () => { reportService = TestBed.inject(ReportService) as jasmine.SpyObj; tasksService = TestBed.inject(TasksService) as jasmine.SpyObj; orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; + categoriesService = TestBed.inject(CategoriesService) as jasmine.SpyObj; apiV2Service = TestBed.inject(ApiV2Service) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; networkService = TestBed.inject(NetworkService) as jasmine.SpyObj; @@ -390,6 +404,7 @@ describe('MyExpensesPage', () => { platformHandlerService.registerBackButtonAction.and.returnValue(backButtonSubscription); orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); orgSettingsService.get.and.returnValue(of(orgSettingsRes)); + categoriesService.getMileageOrPerDiemCategories.and.returnValue(of(mileagePerDiemPlatformCategoryData)); corporateCreditCardService.getAssignedCards.and.returnValue(of(expectedAssignedCCCStats)); spyOn(component, 'getCardDetail').and.returnValue(expectedUniqueCardStats); spyOn(component, 'syncOutboxExpenses'); @@ -549,7 +564,7 @@ describe('MyExpensesPage', () => { component.ionViewWillEnter(); tick(500); - expect(component.setupActionSheet).toHaveBeenCalledOnceWith(orgSettingsRes); + expect(component.setupActionSheet).toHaveBeenCalledOnceWith(orgSettingsRes, allowedExpenseTypes); })); it('should update cardNumbers by calling getCardDetail', fakeAsync(() => { @@ -1100,10 +1115,28 @@ describe('MyExpensesPage', () => { }); }); }); - it('setupActionSheet(): should update actionSheetButtons', () => { - spyOn(component, 'actionSheetButtonsHandler'); - component.setupActionSheet(orgSettingsRes); - expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonRes); + describe('setupActionSheet()', () => { + it('should update actionSheetButtons', () => { + spyOn(component, 'actionSheetButtonsHandler'); + component.setupActionSheet(orgSettingsRes, allowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonRes); + }); + + it('should update actionSheetButtons without mileage', () => { + spyOn(component, 'actionSheetButtonsHandler'); + const mockAllowedExpenseTypes = clone(allowedExpenseTypes); + mockAllowedExpenseTypes.mileage = false; + component.setupActionSheet(orgSettingsRes, mockAllowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonsWithPerDiem); + }); + + it('should update actionSheetButtons without Per Diem', () => { + spyOn(component, 'actionSheetButtonsHandler'); + const mockAllowedExpenseTypes = clone(allowedExpenseTypes); + mockAllowedExpenseTypes.perDiem = false; + component.setupActionSheet(orgSettingsRes, mockAllowedExpenseTypes); + expect(component.actionSheetButtons).toEqual(expectedActionSheetButtonsWithMileage); + }); }); describe('actionSheetButtonsHandler():', () => { diff --git a/src/app/fyle/my-expenses/my-expenses.page.ts b/src/app/fyle/my-expenses/my-expenses.page.ts index 40ba68f199..8eb53f3d9d 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.ts +++ b/src/app/fyle/my-expenses/my-expenses.page.ts @@ -76,6 +76,8 @@ import { UniqueCardStats } from 'src/app/core/models/unique-cards-stats.model'; import { FilterQueryParams } from 'src/app/core/models/filter-query-params.model'; import { SelectedFilters } from 'src/app/shared/components/fy-filters/selected-filters.interface'; import { UniqueCards } from 'src/app/core/models/unique-cards.model'; +import { CategoriesService } from 'src/app/core/services/categories.service'; +import { PlatformCategory } from 'src/app/core/models/platform/platform-category.model'; @Component({ selector: 'app-my-expenses', @@ -115,6 +117,10 @@ export class MyExpensesPage implements OnInit { isPerDiemEnabled$: Observable; + orgSettings$: Observable; + + specialCategories$: Observable; + pendingTransactions: Partial[] = []; selectionMode = false; @@ -208,6 +214,7 @@ export class MyExpensesPage implements OnInit { private currencyService: CurrencyService, private orgUserSettingsService: OrgUserSettingsService, private platformHandlerService: PlatformHandlerService, + private categoriesService: CategoriesService, private navController: NavController ) {} @@ -337,10 +344,10 @@ export class MyExpensesPage implements OnInit { }; } - setupActionSheet(orgSettings: OrgSettings): void { + setupActionSheet(orgSettings: OrgSettings, allowedExpenseTypes: Record): void { const that = this; - const mileageEnabled = orgSettings.mileage.enabled; - const isPerDiemEnabled = orgSettings.per_diem.enabled; + const mileageEnabled = orgSettings.mileage.enabled && allowedExpenseTypes.mileage; + const isPerDiemEnabled = orgSettings.per_diem.enabled && allowedExpenseTypes.perDiem; that.actionSheetButtons = [ { text: 'Capture Receipt', @@ -357,7 +364,7 @@ export class MyExpensesPage implements OnInit { ]; if (mileageEnabled) { - this.actionSheetButtons.push({ + that.actionSheetButtons.push({ text: 'Add Mileage', icon: 'assets/svg/fy-mileage.svg', cssClass: 'capture-receipt', @@ -428,14 +435,25 @@ export class MyExpensesPage implements OnInit { map((orgUserSettings) => orgUserSettings?.bulk_fyle_settings?.enabled) ); - const getOrgSettingsService$ = this.orgSettingsService.get().pipe(shareReplay(1)); + this.orgSettings$ = this.orgSettingsService.get().pipe(shareReplay(1)); + this.specialCategories$ = this.categoriesService.getMileageOrPerDiemCategories().pipe(shareReplay(1)); - this.isMileageEnabled$ = getOrgSettingsService$.pipe(map((orgSettings) => orgSettings?.mileage?.enabled)); - this.isPerDiemEnabled$ = getOrgSettingsService$.pipe(map((orgSettings) => orgSettings?.per_diem?.enabled)); + this.isMileageEnabled$ = this.orgSettings$.pipe(map((orgSettings) => orgSettings?.mileage?.enabled)); + this.isPerDiemEnabled$ = this.orgSettings$.pipe(map((orgSettings) => orgSettings?.per_diem?.enabled)); - getOrgSettingsService$.subscribe((orgSettings) => { + this.orgSettings$.subscribe((orgSettings) => { this.isNewReportsFlowEnabled = orgSettings?.simplified_report_closure_settings?.enabled || false; - this.setupActionSheet(orgSettings); + }); + + forkJoin({ + orgSettings: this.orgSettings$, + specialCategories: this.specialCategories$, + }).subscribe(({ orgSettings, specialCategories }) => { + const allowedExpenseTypes = { + mileage: specialCategories.some((category) => category.system_category === 'Mileage'), + perDiem: specialCategories.some((category) => category.system_category === 'Per Diem'), + }; + this.setupActionSheet(orgSettings, allowedExpenseTypes); }); forkJoin({