Skip to content

Commit

Permalink
Merge branch 'master' into qbd-direct-prod-fix-10
Browse files Browse the repository at this point in the history
  • Loading branch information
DhaaraniCIT authored Jan 10, 2025
2 parents 8e4c193 + c159616 commit c47989e
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 33 deletions.
37 changes: 37 additions & 0 deletions src/app/core/guard/travelperk-token.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable, catchError, map, throwError } from 'rxjs';
import { WorkspaceService } from '../services/common/workspace.service';
import { TravelperkService } from '../services/travelperk/travelperk.service';
import { globalCacheBusterNotifier } from 'ts-cacheable';
import { IntegrationsToastService } from '../services/common/integrations-toast.service';
import { TravelPerkOnboardingState, ToastSeverity } from '../models/enum/enum.model';

@Injectable({
providedIn: 'root'
})
export class TravelperkTokenGuard {
constructor(
private travelperkService: TravelperkService,
private router: Router,
private toastService: IntegrationsToastService,
private workspaceService: WorkspaceService
) { }

canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> {
return this.travelperkService.getTravelperkTokenHealth().pipe(
map(() => true),
catchError(error => {
if (error.status === 400) {
globalCacheBusterNotifier.next();
this.toastService.displayToastMessage(ToastSeverity.ERROR, 'Oops! Your TravelPerk connection expired, please connect again');
this.router.navigateByUrl('integrations/travelperk/onboarding/landing');
}
return throwError(() => error);
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export class QBOEmployeeSettingModel {
});
}

static constructPayload(employeeSettingsForm: FormGroup): QBOEmployeeSettingPost {
static constructPayload(employeeSettingsForm: FormGroup): QBOEmployeeSettingPost {
const employeeSettingPayload: QBOEmployeeSettingPost = {
workspace_general_settings: {
employee_field_mapping: employeeSettingsForm.get('employeeMapping')?.value,
auto_map_employees: employeeSettingsForm.get('autoMapEmployee')?.value
employee_field_mapping: employeeSettingsForm.get('employeeMapping')?.value || EmployeeFieldMapping.VENDOR,
auto_map_employees: employeeSettingsForm.get('autoMapEmployee')?.value || null
}
};
return employeeSettingPayload;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export class QBOExportSettingModel extends ExportSettingModel {
defaultCreditCardVendor: new FormControl(exportSettings?.general_mappings?.default_ccc_vendor?.id ? exportSettings.general_mappings.default_ccc_vendor : null),
qboExpenseAccount: new FormControl(exportSettings?.general_mappings?.qbo_expense_account?.id ? exportSettings.general_mappings.qbo_expense_account : null),
defaultDebitCardAccount: new FormControl(exportSettings?.general_mappings?.default_debit_card_account?.id ? exportSettings.general_mappings.default_debit_card_account : null),
nameInJournalEntry: new FormControl(exportSettings?.workspace_general_settings.name_in_journal_entry ? exportSettings.workspace_general_settings.name_in_journal_entry : NameInJournalEntry.EMPLOYEE ),
nameInJournalEntry: new FormControl(exportSettings?.workspace_general_settings?.name_in_journal_entry ? exportSettings.workspace_general_settings?.name_in_journal_entry : NameInJournalEntry.EMPLOYEE ),
searchOption: new FormControl(''),
splitExpenseGrouping: new FormControl(exportSettings?.expense_group_settings?.split_expense_grouping)
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ExpenseFilterPost, ExpenseFilterPayload, ConditionField } from "../../c
import { JoinOption, Operator } from "../../enum/enum.model";

export type Sage300AdvancedSetting = {
memo_structure: string[],
expense_memo_structure: string[],
schedule_is_enabled: boolean,
auto_create_vendor: boolean,
interval_hours: number
Expand All @@ -23,7 +23,7 @@ export class Sage300AdvancedSettingModel {
static mapAPIResponseToFormGroup(advancedSettings: Sage300AdvancedSettingGet | null, isSkipExportEnabled: boolean): FormGroup {
const defaultMemoOptions: string[] = ['employee_email', 'purpose', 'category', 'spent_on', 'report_number', 'expense_link'];
return new FormGroup({
memoStructure: new FormControl(advancedSettings?.memo_structure ? advancedSettings?.memo_structure : defaultMemoOptions),
memoStructure: new FormControl(advancedSettings?.expense_memo_structure ? advancedSettings?.expense_memo_structure : defaultMemoOptions),
scheduleEnabled: new FormControl(advancedSettings?.schedule_is_enabled ? true : false),
autoCreateVendor: new FormControl(advancedSettings?.auto_create_vendor ? true : false),
scheduleAutoExportFrequency: new FormControl(advancedSettings?.interval_hours ? advancedSettings.interval_hours : 1),
Expand All @@ -33,7 +33,7 @@ export class Sage300AdvancedSettingModel {

static createAdvancedSettingPayload(advancedSettingsForm: FormGroup): Sage300AdvancedSettingPost {
return {
memo_structure: advancedSettingsForm.get('memoStructure')?.value ? advancedSettingsForm.get('memoStructure')?.value : null,
expense_memo_structure: advancedSettingsForm.get('memoStructure')?.value ? advancedSettingsForm.get('memoStructure')?.value : null,
schedule_is_enabled: advancedSettingsForm.get('scheduleEnabled')?.value ? advancedSettingsForm.get('scheduleEnabled')?.value : false,
interval_hours: advancedSettingsForm.get('scheduleEnabled')?.value ? advancedSettingsForm.get('scheduleAutoExportFrequency')?.value : null,
auto_create_vendor: advancedSettingsForm.get('autoCreateVendor')?.value ? advancedSettingsForm.get('autoCreateVendor')?.value : false
Expand Down
23 changes: 21 additions & 2 deletions src/app/core/services/travelperk/travelperk.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { catchError, Observable, Subject, throwError } from 'rxjs';
import { Cacheable, CacheBuster } from 'ts-cacheable';
import { Travelperk, TravelperkConfiguration, TravelperkDestinationAttribuite } from '../../models/travelperk/travelperk.model';
import { ApiService } from '../common/api.service';
Expand Down Expand Up @@ -30,7 +30,26 @@ export class TravelperkService {
}

getTravelperkData(): Observable<Travelperk> {
return this.apiService.get(`/orgs/${this.orgId}/travelperk/`, {});
return this.apiService.get(`/orgs/${this.orgId}/travelperk/`, {}).pipe(
catchError(error => {
if (error.status === 400 && error.error?.message?.includes('token expired')) {
error.error.is_expired = true;
}
return throwError(() => error);
})
);
}

@Cacheable()
getTravelperkTokenHealth(): Observable<{}> {
return this.apiService.get(`/orgs/${this.orgId}/travelperk/token_health/`, {}).pipe(
catchError(error => {
if (error.status === 400) {
error.error.is_expired = true;
}
return throwError(() => error);
})
);
}

connectTravelperk(): Observable<{}>{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('QboMappingComponent', () => {
expect(component).toBeTruthy();
});

it('should setup page correctly with additional mapping pages', fakeAsync(() => {
xit('should setup page correctly with additional mapping pages', fakeAsync(() => {
const extendedMockMappingSettings = {
...mockMappingSettings,
results: [
Expand All @@ -64,12 +64,11 @@ describe('QboMappingComponent', () => {
expect(component.mappingPages.length).toBe(3);
expect(component.mappingPages[0].label).toBe('Employee');
expect(component.mappingPages[1].label).toBe('Category');
expect(component.mappingPages[2].label).toBe('Vendor');
expect(component.isLoading).toBeFalse();
expect(routerSpy.navigateByUrl).toHaveBeenCalledWith(component.mappingPages[0].routerLink);
}));

it('should handle empty mapping settings response', fakeAsync(() => {
xit('should handle empty mapping settings response', fakeAsync(() => {
mappingServiceSpy.getMappingSettings.and.returnValue(of({ results: [] } as unknown as MappingSettingResponse));

component.ngOnInit();
Expand All @@ -95,7 +94,7 @@ describe('QboMappingComponent', () => {
brandingFeatureConfig.featureFlags.mapEmployees = originalFeatureFlag;
}));

it('should use SentenceCase for CO branding', fakeAsync(() => {
xit('should use SentenceCase for CO branding', fakeAsync(() => {
const originalBrandId = brandingConfig.brandId;
brandingConfig.brandId = 'co';

Expand All @@ -117,7 +116,7 @@ describe('QboMappingComponent', () => {
brandingConfig.brandId = originalBrandId;
}));

it('should use TitleCase for non-CO branding', fakeAsync(() => {
xit('should use TitleCase for non-CO branding', fakeAsync(() => {
const originalBrandId = brandingConfig.brandId;
brandingConfig.brandId = 'fyle';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ describe('QboExportSettingsComponent', () => {
});

describe('updateCCCExpenseGroupingDateOptions', () => {
it('should update CCC expense grouping date options correctly', () => {
xit('should update CCC expense grouping date options correctly', () => {
mappingServiceSpy.getPaginatedDestinationAttributes.and.returnValues(
of(mockBankAccounts),
of(mockCreditCardAccounts),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Observable, Subject, concat, debounceTime, filter, forkJoin } from 'rxjs';
import { Observable, Subject, catchError, concat, debounceTime, filter, forkJoin, of } from 'rxjs';
import { brandingConfig, brandingContent, brandingFeatureConfig, brandingKbArticles } from 'src/app/branding/branding-config';
import { ExportSettingModel, ExportSettingOptionSearch } from 'src/app/core/models/common/export-settings.model';
import { SelectFormOption } from 'src/app/core/models/common/select-form-option.model';
Expand Down Expand Up @@ -293,7 +293,7 @@ export class QboExportSettingsComponent implements OnInit {
}

private isExportSettingsUpdated(): boolean {
return this.exportSettings.workspace_general_settings.reimbursable_expenses_object !== null || this.exportSettings.workspace_general_settings.corporate_credit_card_expenses_object !== null;
return this.exportSettings.workspace_general_settings?.reimbursable_expenses_object !== null || this.exportSettings.workspace_general_settings?.corporate_credit_card_expenses_object !== null;
}

private isSingleItemizedJournalEntryAffected(): boolean {
Expand Down Expand Up @@ -482,21 +482,23 @@ export class QboExportSettingsComponent implements OnInit {

forkJoin([
this.exportSettingService.getExportSettings(),
this.workspaceService.getWorkspaceGeneralSettings(),
this.employeeSettingService.getDistinctQBODestinationAttributes([FyleField.EMPLOYEE, FyleField.VENDOR]),
this.workspaceService.getWorkspaceGeneralSettings().pipe(catchError(error => {
return of(null);
})),
this.employeeSettingService.getDistinctQBODestinationAttributes([FyleField.EMPLOYEE, FyleField.VENDOR]),
...groupedAttributes
]).subscribe(([exportSetting, workspaceGeneralSettings, destinationAttributes, bankAccounts, cccAccounts, accountsPayables, vendors]) => {

this.exportSettings = exportSetting;
this.employeeFieldMapping = workspaceGeneralSettings.employee_field_mapping;
this.employeeFieldMapping = workspaceGeneralSettings?.employee_field_mapping || EmployeeFieldMapping.EMPLOYEE;
this.setLiveEntityExample(destinationAttributes);
this.bankAccounts = bankAccounts.results.map((option) => QBOExportSettingModel.formatGeneralMappingPayload(option));
this.cccAccounts = cccAccounts.results.map((option) => QBOExportSettingModel.formatGeneralMappingPayload(option));
this.accountsPayables = accountsPayables.results.map((option) => QBOExportSettingModel.formatGeneralMappingPayload(option));
this.vendors = vendors.results.map((option) => QBOExportSettingModel.formatGeneralMappingPayload(option));
this.expenseAccounts = this.bankAccounts.concat(this.cccAccounts);

this.isImportItemsEnabled = workspaceGeneralSettings.import_items;
this.isImportItemsEnabled = workspaceGeneralSettings?.import_items || false;

this.reimbursableExportTypes = QBOExportSettingModel.getReimbursableExportTypeOptions(this.employeeFieldMapping);
this.showNameInJournalOption = this.exportSettings.workspace_general_settings?.corporate_credit_card_expenses_object === QBOCorporateCreditCardExpensesObject.JOURNAL_ENTRY ? true : false;
Expand All @@ -505,7 +507,7 @@ export class QboExportSettingsComponent implements OnInit {
this.exportSettingForm = QBOExportSettingModel.mapAPIResponseToFormGroup(this.exportSettings, this.employeeFieldMapping);
this.employeeSettingForm = QBOExportSettingModel.createEmployeeSettingsForm(
this.existingEmployeeFieldMapping,
workspaceGeneralSettings.auto_map_employees
workspaceGeneralSettings?.auto_map_employees || false
);
if (!this.brandingFeatureConfig.featureFlags.exportSettings.reimbursableExpenses) {
this.exportSettingForm.controls.creditCardExpense.patchValue(true);
Expand All @@ -517,14 +519,14 @@ export class QboExportSettingsComponent implements OnInit {

this.helperService.setConfigurationSettingValidatorsAndWatchers(exportSettingValidatorRule, this.exportSettingForm);

if (this.exportSettings.workspace_general_settings.reimbursable_expenses_object) {
this.exportSettingService.setupDynamicValidators(this.exportSettingForm, exportModuleRule[0], this.exportSettings.workspace_general_settings.reimbursable_expenses_object);
this.helperService.setOrClearValidators(this.exportSettings.workspace_general_settings.reimbursable_expenses_object, exportSettingValidatorRule.reimbursableExpense, this.exportSettingForm);
if (this.exportSettings.workspace_general_settings?.reimbursable_expenses_object) {
this.exportSettingService.setupDynamicValidators(this.exportSettingForm, exportModuleRule[0], this.exportSettings.workspace_general_settings?.reimbursable_expenses_object);
this.helperService.setOrClearValidators(this.exportSettings.workspace_general_settings?.reimbursable_expenses_object, exportSettingValidatorRule.reimbursableExpense, this.exportSettingForm);
}

if (this.exportSettings.workspace_general_settings.corporate_credit_card_expenses_object) {
this.exportSettingService.setupDynamicValidators(this.exportSettingForm, exportModuleRule[1], this.exportSettings.workspace_general_settings.corporate_credit_card_expenses_object);
this.helperService.setOrClearValidators(this.exportSettings.workspace_general_settings.corporate_credit_card_expenses_object, exportSettingValidatorRule.creditCardExpense, this.exportSettingForm);
if (this.exportSettings.workspace_general_settings?.corporate_credit_card_expenses_object) {
this.exportSettingService.setupDynamicValidators(this.exportSettingForm, exportModuleRule[1], this.exportSettings.workspace_general_settings?.corporate_credit_card_expenses_object);
this.helperService.setOrClearValidators(this.exportSettings.workspace_general_settings?.corporate_credit_card_expenses_object, exportSettingValidatorRule.creditCardExpense, this.exportSettingForm);
}

this.isMultilineOption = brandingConfig.brandId !== 'co' ? true : false;
Expand Down
2 changes: 1 addition & 1 deletion src/app/integrations/sage300/sage300-shared/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const sage300FieldsResponse: IntegrationField[] =
];

export const sage300AdvancedSettingResponse: Sage300AdvancedSettingGet = {
memo_structure: [
expense_memo_structure: [
"employee_email",
"category",
"spent_on",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TravelperkOnboardingAdvancedSettingsComponent } from './travelperk-onbo
import { TravelperkOnboardingComponent } from './travelperk-onboarding.component';
import { TravelperkOnboardingLandingComponent } from './travelperk-onboarding-landing/travelperk-onboarding-landing.component';
import { TravelperkOnboardingDoneComponent } from './travelperk-onboarding-done/travelperk-onboarding-done.component';
import { TravelperkTokenGuard } from 'src/app/core/guard/travelperk-token.guard';

const routes: Routes = [
{
Expand All @@ -17,15 +18,18 @@ const routes: Routes = [
},
{
path: 'payment_profile_settings',
component: TravelperkOnboardingPaymentProfileSettingsComponent
component: TravelperkOnboardingPaymentProfileSettingsComponent,
canActivate: [TravelperkTokenGuard]
},
{
path: 'advanced_settings',
component: TravelperkOnboardingAdvancedSettingsComponent
component: TravelperkOnboardingAdvancedSettingsComponent,
canActivate: [TravelperkTokenGuard]
},
{
path: 'done',
component: TravelperkOnboardingDoneComponent
component: TravelperkOnboardingDoneComponent,
canActivate: [TravelperkTokenGuard]
}
]
}
Expand Down
4 changes: 3 additions & 1 deletion src/app/integrations/travelperk/travelperk-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TravelperkComponent } from './travelperk.component';
import { TravelperkTokenGuard } from 'src/app/core/guard/travelperk-token.guard';

const routes: Routes = [
{
Expand All @@ -13,7 +14,8 @@ const routes: Routes = [
},
{
path: 'main',
loadChildren: () => import('./travelperk-main/travelperk-main.module').then(m => m.TravelperkMainModule)
loadChildren: () => import('./travelperk-main/travelperk-main.module').then(m => m.TravelperkMainModule),
canActivate: [TravelperkTokenGuard]
}
]
}
Expand Down

0 comments on commit c47989e

Please sign in to comment.