Skip to content

Commit

Permalink
feat: new split expense trackers (#2743)
Browse files Browse the repository at this point in the history
* feat: new split expense trackers

* fix: file not attached in split expense fix (#2746)

* minor
  • Loading branch information
suyashpatil78 committed Feb 15, 2024
1 parent 36c8e76 commit 6ace330
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 18 deletions.
10 changes: 9 additions & 1 deletion src/app/core/models/tracking-properties.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Transaction } from './v1/transaction.model';
import { ExpenseView } from './expense-view.enum';
import { TaskFilters } from './task-filters.model';
import { CardNetworkType } from '../enums/card-network-type';
import { SplitPayload } from './platform/v1/split-payload.model';

export interface TrackingMethods {
identify<T, K>(data: T, property?: K): void;
Expand Down Expand Up @@ -39,8 +40,15 @@ export interface ExpenseProperties {
}

export interface SplittingExpenseProperties {
'Split Type': string;
Type: string;
'Is Evenly Split': boolean;
Asset: string;
'Is part of report': boolean;
'Report ID': string;
'Expense State': string;
'User Role': string;
'Error Message'?: string;
'Split Payload'?: SplitPayload;
}

export interface PolicyCorrectionProperties {
Expand Down
16 changes: 16 additions & 0 deletions src/app/core/services/tracking.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ export class TrackingService {
this.eventTrack('Splitting Expense', properties);
}

splitExpenseSuccess(properties: SplittingExpenseProperties): void {
this.eventTrack('Split Expense Success', properties);
}

splitExpenseFailed(properties: SplittingExpenseProperties): void {
this.eventTrack('Split Expense Failed', properties);
}

splitExpensePolicyCheckFailed(properties: SplittingExpenseProperties): void {
this.eventTrack('Split Expense Policy check failed', properties);
}

splitExpensePolicyAndMissingFieldsPopupShown(properties: SplittingExpenseProperties): void {
this.eventTrack('Split Expense Policy and Missing Fields Popup Shown', properties);
}

// view expense event
viewExpense(properties: { Type: string }): void {
this.eventTrack('View Expense', properties);
Expand Down
41 changes: 36 additions & 5 deletions src/app/fyle/split-expense/split-expense.page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ import {
} from 'src/app/core/mock-data/transformed-split-expense-missing-fields.data';
import { splitPolicyExp1 } from 'src/app/core/mock-data/split-expense-policy.data';
import { SplitExpenseMissingFieldsData } from 'src/app/core/models/split-expense-missing-fields.data';
import { splitPayloadData1 } from 'src/app/core/mock-data/split-payload.data';

describe('SplitExpensePage', () => {
let component: SplitExpensePage;
Expand Down Expand Up @@ -218,6 +219,7 @@ describe('SplitExpensePage', () => {
'filteredPolicyViolations',
'handlePolicyAndMissingFieldsCheck',
'checkIfMissingFieldsExist',
'transformSplitTo',
]);
const currencyServiceSpy = jasmine.createSpyObj('CurrencyService', ['getHomeCurrency']);
const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['delete', 'matchCCCExpense']);
Expand All @@ -227,7 +229,14 @@ describe('SplitExpensePage', () => {
const reportServiceSpy = jasmine.createSpyObj('ReportService', ['addTransactions']);
const matSnackBarSpy = jasmine.createSpyObj('MatSnackBar', ['openFromComponent']);
const snackbarPropertiesSpy = jasmine.createSpyObj('SnackbarPropertiesService', ['setSnackbarProperties']);
const trackingServiceSpy = jasmine.createSpyObj('TrackingService', ['showToastMessage', 'splittingExpense']);
const trackingServiceSpy = jasmine.createSpyObj('TrackingService', [
'showToastMessage',
'splittingExpense',
'splitExpenseSuccess',
'splitExpenseFailed',
'splitExpensePolicyCheckFailed',
'splitExpensePolicyAndMissingFieldsPopupShown',
]);
const policyServiceSpy = jasmine.createSpyObj('PolicyService', ['checkIfViolationsExist']);
const modalControllerSpy = jasmine.createSpyObj('ModalController', ['create', 'getTop']);
const modalPropertiesSpy = jasmine.createSpyObj('ModalPropertiesService', ['getModalDefaultProperties']);
Expand Down Expand Up @@ -1754,8 +1763,13 @@ describe('SplitExpensePage', () => {
transformedOrgCategories
);
expect(trackingService.splittingExpense).toHaveBeenCalledOnceWith({
'Split Type': 'projects',
Type: 'projects',
'Is Evenly Split': true,
Asset: 'Mobile',
'Is part of report': false,
'Report ID': null,
'User Role': 'spender',
'Expense State': 'DRAFT',
});
expect(component.handleSplitExpensePolicyViolations).toHaveBeenCalledOnceWith(policyVoilationData2);
});
Expand All @@ -1781,8 +1795,13 @@ describe('SplitExpensePage', () => {
);
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'my_expenses']);
expect(trackingService.splittingExpense).toHaveBeenCalledOnceWith({
'Split Type': 'projects',
Type: 'projects',
'Is Evenly Split': true,
Asset: 'Mobile',
'Is part of report': false,
'Report ID': null,
'User Role': 'spender',
'Expense State': 'DRAFT',
});
expect(component.handleSplitExpensePolicyViolations).not.toHaveBeenCalled();
}
Expand Down Expand Up @@ -2254,8 +2273,13 @@ describe('SplitExpensePage', () => {
expect(component.generateSplitEtxnFromFg).toHaveBeenCalledOnceWith(component.splitExpensesFormArray.value[0]);
expect(component.handlePolicyAndMissingFieldsCheck).toHaveBeenCalledOnceWith(txnList);
expect(trackingService.splittingExpense).toHaveBeenCalledOnceWith({
'Split Type': 'projects',
Type: 'projects',
'Is Evenly Split': true,
Asset: 'Mobile',
'Is part of report': false,
'Report ID': null,
'User Role': 'spender',
'Expense State': 'DRAFT',
});
expect(component.handleSplitExpense).toHaveBeenCalledOnceWith({ '0': 'test comment' });
});
Expand All @@ -2268,6 +2292,7 @@ describe('SplitExpensePage', () => {
.createSpy()
.and.returnValue(throwError(() => new Error('Policy Violation checks were failed!')));
spyOn(component, 'toastWithoutCTA');
splitExpenseService.transformSplitTo.and.returnValue(splitPayloadData1);

try {
component.saveV2();
Expand All @@ -2280,8 +2305,13 @@ describe('SplitExpensePage', () => {
'msb-failure-with-camera-icon'
);
expect(trackingService.splittingExpense).toHaveBeenCalledOnceWith({
'Split Type': 'projects',
Type: 'projects',
'Is Evenly Split': true,
Asset: 'Mobile',
'Is part of report': false,
'Report ID': null,
'User Role': 'spender',
'Expense State': 'DRAFT',
});
expect(component.handleSplitExpense).not.toHaveBeenCalled();
}
Expand Down Expand Up @@ -2396,6 +2426,7 @@ describe('SplitExpensePage', () => {
handle: false,
};
modalProperties.getModalDefaultProperties.and.returnValue(properties);
component.transaction = cloneDeep(txnData4);
});

it('should open policy violations and missing fields modal', async () => {
Expand Down
56 changes: 44 additions & 12 deletions src/app/fyle/split-expense/split-expense.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import { TransformedSplitExpenseMissingFields } from 'src/app/core/models/transf
import { SplitExpenseViolationsPopup } from 'src/app/core/models/split-expense-violations-popup.model';
import { TimezoneService } from 'src/app/core/services/timezone.service';
import { TxnCustomProperties } from 'src/app/core/models/txn-custom-properties.model';
import { SplittingExpenseProperties } from 'src/app/core/models/tracking-properties.model';
import { HttpErrorResponse } from '@angular/common/http';

@Component({
selector: 'app-split-expense',
Expand Down Expand Up @@ -640,6 +642,9 @@ export class SplitExpensePage {
filteredMissingFieldsViolations = null;
}

const splitTrackingProps = this.getSplitExpensePoperties();
this.trackingService.splitExpensePolicyAndMissingFieldsPopupShown(splitTrackingProps);

const splitExpenseViolationsModal = await this.modalController.create({
component: SplitExpensePolicyViolationComponent,
componentProps: {
Expand Down Expand Up @@ -717,14 +722,21 @@ export class SplitExpensePage {
this.splitExpenseService
.splitExpense(this.formattedSplitExpense, this.fileObjs, this.transaction, reportAndCategoryParams)
.pipe(
catchError((err) => {
catchError((errResponse: HttpErrorResponse) => {
const splitTrackingProps = this.getSplitExpensePoperties();
splitTrackingProps['Error Message'] = (errResponse?.error as { message: string })?.message;
this.trackingService.splitExpensePolicyCheckFailed(splitTrackingProps);

const message = 'We were unable to split your expense. Please try again later.';
this.toastWithoutCTA(message, ToastType.FAILURE, 'msb-failure-with-camera-icon');
this.router.navigate(['/', 'enterprise', 'my_expenses']);
return throwError(err);
return throwError(errResponse);
})
)
.subscribe((txns) => {
const splitTrackingProps = this.getSplitExpensePoperties();
this.trackingService.splitExpenseSuccess(splitTrackingProps);

const txnIds = txns.data.map((txn) => txn.id);

if (comments) {
Expand Down Expand Up @@ -759,6 +771,18 @@ export class SplitExpensePage {
}
}

getSplitExpensePoperties(): SplittingExpenseProperties {
return {
Type: this.splitType,
'Is Evenly Split': this.isEvenlySplit(),
Asset: 'Mobile',
'Is part of report': !!this.reportId,
'Report ID': this.reportId || null,
'Expense State': this.transaction.state,
'User Role': 'spender',
};
}

saveV2(): void {
if (this.splitExpensesFormArray.valid) {
this.showErrorBlock = false;
Expand Down Expand Up @@ -801,6 +825,7 @@ export class SplitExpensePage {

forkJoin({
generatedSplitEtxn: forkJoin(generatedSplitEtxn$),
files: this.uploadFiles(this.fileUrls),
})
.pipe(
concatMap(({ generatedSplitEtxn }) => this.createSplitTxns(generatedSplitEtxn)),
Expand All @@ -809,18 +834,28 @@ export class SplitExpensePage {
this.correctTotalSplitAmount();
return this.handlePolicyAndMissingFieldsCheck(formattedSplitExpense);
}),
catchError((err) => {
catchError((errResponse: HttpErrorResponse) => {
const splitTrackingProps = this.getSplitExpensePoperties();
splitTrackingProps['Error Message'] = (errResponse?.error as { message: string })?.message;

const fileIds = this.fileObjs?.map((file) => file.id);
splitTrackingProps['Split Payload'] = this.splitExpenseService.transformSplitTo(
this.formattedSplitExpense,
this.transaction,
fileIds,
{ reportId: this.reportId, unspecifiedCategory: this.unspecifiedCategory }
);

this.trackingService.splitExpensePolicyCheckFailed(splitTrackingProps);

const message = 'We were unable to split your expense. Please try again later.';
this.toastWithoutCTA(message, ToastType.FAILURE, 'msb-failure-with-camera-icon');
return throwError(err);
return throwError(errResponse);
}),
finalize(() => {
this.saveSplitExpenseLoading = false;

const splitTrackingProps = {
'Split Type': this.splitType,
'Is Evenly Split': this.isEvenlySplit(),
};
const splitTrackingProps = this.getSplitExpensePoperties();
this.trackingService.splittingExpense(splitTrackingProps);
})
)
Expand Down Expand Up @@ -911,10 +946,7 @@ export class SplitExpensePage {
finalize(() => {
this.saveSplitExpenseLoading = false;

const splitTrackingProps = {
'Split Type': this.splitType,
'Is Evenly Split': this.isEvenlySplit(),
};
const splitTrackingProps = this.getSplitExpensePoperties();
this.trackingService.splittingExpense(splitTrackingProps);
})
)
Expand Down

0 comments on commit 6ace330

Please sign in to comment.